Merge branch 'hotfix/LIL-601' into 'master'

Hotfix/lil 601

See merge request lilcity/backend!141
remotes/origin/hotfix/LIL-691
cfwme 7 years ago
commit c86302d4c6
  1. 2
      api/v1/serializers/course.py
  2. 3
      apps/course/filters.py
  3. 18
      apps/course/migrations/0044_course_age.py
  4. 18
      apps/course/models.py
  5. 2
      apps/course/templates/course/_items.html
  6. 7
      apps/course/templates/course/course.html
  7. 23
      apps/course/templates/course/course_only_lessons.html
  8. 25
      apps/course/templates/course/courses.html
  9. 6
      apps/course/templates/course/inclusion/category_items.html
  10. 14
      apps/course/templates/course/lesson.html
  11. 13
      apps/course/views.py
  12. 2
      apps/school/templates/school/livelesson_detail.html
  13. 2
      apps/school/templates/school/livelessons_list.html
  14. 118
      project/templates/blocks/gallery.html
  15. 5
      project/templates/blocks/last_courses.html
  16. 5
      project/templates/blocks/lil_store_js.html
  17. 2
      project/templates/blocks/popup_course_lock.html
  18. 2
      project/templates/lilcity/main.html
  19. 2
      project/views.py
  20. 94
      web/src/components/CourseRedactor.vue
  21. 5
      web/src/components/LessonRedactor.vue
  22. 28
      web/src/components/inputs/LilSelect.vue
  23. 4
      web/src/components/redactor/VueRedactor.vue
  24. 1
      web/src/js/modules/api.js
  25. 112
      web/src/js/modules/courses.js
  26. 38
      web/src/sass/_common.sass

@ -137,6 +137,7 @@ class CourseCreateSerializer(DispatchContentMixin,
'from_author',
'cover',
'price',
'age',
'is_infinite',
'deferred_start_at',
'category',
@ -288,6 +289,7 @@ class CourseSerializer(serializers.ModelSerializer):
'from_author',
'cover',
'price',
'age',
'is_infinite',
'deferred_start_at',
'category',

@ -5,7 +5,8 @@ from .models import Course
class CourseFilter(django_filters.FilterSet):
category = django_filters.CharFilter(field_name='category')
age = django_filters.ChoiceFilter(field_name='age', choices=Course.AGE_CHOICES)
class Meta:
model = Course
fields = ['category']
fields = ['category', 'age']

@ -0,0 +1,18 @@
# Generated by Django 2.0.6 on 2018-09-07 00:20
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('course', '0043_auto_20180824_2132'),
]
operations = [
migrations.AddField(
model_name='course',
name='age',
field=models.SmallIntegerField(choices=[(0, 'Любой возраст'), (1, 'до 5'), (2, '5-7'), (3, '7-9'), (4, '9-12'), (5, '12-15'), (6, '15-18'), (7, 'от 18')], default=0),
),
]

@ -50,6 +50,23 @@ class Course(BaseModel, DeactivatedMixin):
(ARCHIVED, 'Archived'),
(DENIED, 'Denied')
)
AGE_LT5 = 1
AGE_57 = 2
AGE_79 = 3
AGE_912 = 4
AGE_1215 = 5
AGE_1518 = 6
AGE_GT18 = 7
AGE_CHOICES = (
(0, 'Любой возраст'),
(AGE_LT5, 'до 5'),
(AGE_57, '5-7'),
(AGE_79, '7-9'),
(AGE_912, '9-12'),
(AGE_1215, '12-15'),
(AGE_1518, '15-18'),
(AGE_GT18, 'от 18'),
)
slug = models.SlugField(
allow_unicode=True, null=True, blank=True,
max_length=100, unique=True, db_index=True,
@ -73,6 +90,7 @@ class Course(BaseModel, DeactivatedMixin):
'Цена курса', help_text='Если цена не выставлена, то курс бесплатный',
max_digits=10, decimal_places=2, null=True, blank=True
)
age = models.SmallIntegerField(choices=AGE_CHOICES, default=0)
is_infinite = models.BooleanField(default=False)
deferred_start_at = models.DateTimeField(
'Отложенный запуск курса', help_text='Заполнить если курс отложенный',

@ -54,7 +54,7 @@
{% endif %}
</div>
<a class="courses__title" href="{% url 'course' course.id %}?next={{ request.get_full_path }}">{{ course.title }}</a>
<div class="courses__content">{{ course.short_description }}
<div class="courses__content">{{ course.short_description | safe | linebreaks | truncatechars_html:300 }}
</div>
<div class="courses__user user">
<a href="{% if course.author %}{% url 'user' course.author.id %}{% endif %}">

@ -4,14 +4,14 @@
{% load rupluralize from plural %}
{% block title %}{{ course.title }} - {{ block.super }}{% endblock title %}
{% comment %} seo tags {% endcomment %}
{% block description %}{{ course.short_description }}{% endblock description%}
{% block description %}{{ course.short_description | striptags }}{% endblock description%}
{% block twurl %}{{ request.build_absolute_uri }}{% endblock twurl %}
{% block ogtitle %}{{ course.title }} - {{ block.super }}{% endblock ogtitle %}
{% block ogurl %}{{ request.build_absolute_uri }}{% endblock ogurl %}
{% if course.cover and course.cover.image %}
{% block ogimage %}http://{{request.META.HTTP_HOST}}{% if course.cover %}{{ course.cover.image.url }}{% else %}{% static 'img/og_courses.jpg' %}{% endif %}{% endblock ogimage %}
{% endif %}
{% block ogdescription %}{{ course.short_description }}{% endblock ogdescription %}
{% block ogdescription %}{{ course.short_description | striptags }}{% endblock ogdescription %}
{% block content %}
<div class="section section_border course">
@ -25,6 +25,9 @@
</div>
<div class="go__title">Вернуться</div>
</a>
{% if has_full_access %}
<a class="go__btn btn" href="{% url 'course_edit' course.id %}">Редактировать</a>
{% endif %}
{% if course.author != request.user and not paid and course.price %}
<a
class="go__btn btn{% if pending %} btn_gray{% endif %} btn_md"

@ -4,14 +4,14 @@
{% load rupluralize from plural %}
{% block title %}{{ course.title }} - {{ block.super }}{% endblock title %}
{% comment %} seo tags {% endcomment %}
{% block description %}{{ course.short_description }}{% endblock description%}
{% block description %}{{ course.short_description | striptags }}{% endblock description%}
{% block twurl %}{{ request.build_absolute_uri }}{% endblock twurl %}
{% block ogtitle %}{{ course.title }} - {{ block.super }}{% endblock ogtitle %}
{% block ogurl %}{{ request.build_absolute_uri }}{% endblock ogurl %}
{% if course.cover %}
{% block ogimage %}http://{{request.META.HTTP_HOST}}{% if course.cover %}{{ course.cover.image.url }}{% else %}{% static 'img/og_courses.jpg' %}{% endif %}{% endblock ogimage %}
{% endif %}
{% block ogdescription %}{{ course.short_description }}{% endblock ogdescription %}
{% block ogdescription %}{{ course.short_description | striptags }}{% endblock ogdescription %}
{% block content %}
<div class="section section_border">
@ -25,6 +25,9 @@
</div>
<div class="go__title">Вернуться</div>
</a>
{% if has_full_access %}
<a class="go__btn btn" href="{% url 'course_edit' course.id %}">Редактировать</a>
{% endif %}
{% if not paid and course.price and not has_full_access %}
<a
class="go__btn btn{% if pending %} btn_gray{% endif %} btn_md"
@ -174,27 +177,21 @@
<div class="lessons__title title">Содержание курса</div>
<div class="lessons__list">
{% for lesson in lessons %}
{% if course.author == request.user or request.user.role >= request.user.TEACHER_ROLE or paid %}
<a href="{% url 'lesson' pk=lesson.id %}">
{% else %}
<a href="#">
{% endif %}
<div class="lessons__item">
<div class="lessons__row">
<div class="lessons__content-wrapper">
<div class="lessons__subtitle subtitle">{{ forloop.counter }}. {{ lesson.title }}</div>
<div class="lessons__content">{{ lesson.short_description | truncatechars_html:800 | safe }}</div>
<a href="{% url 'lesson' pk=lesson.id %}" class="btn btn_stroke">Перейти к уроку</a>
</div>
<div class="lessons__preview">
<div class="lessons__pic-wrapper">
<img class="lessons__pic"
src="{% if lesson.cover %}{{ lesson.cover.image_thumbnail.url }}{% else %}{% static 'img/no_cover.png' %}{% endif %}">
</div>
</div>
<div>
<div class="lessons__subtitle subtitle">{{ forloop.counter }}. {{ lesson.title }}</div>
<div class="lessons__content">{{ lesson.short_description | truncatechars_html:800 | safe }}</div>
<a href="{% url 'lesson' pk=lesson.id %}" class="btn btn_stroke">Перейти к уроку</a>
</div>
</div>
</div>
</a>
{% endfor %}
</div>
</div>

@ -23,15 +23,32 @@
<div class="field__select select js-select{% if category %} selected{% endif %}" data-category-select>
<div class="select__head js-select-head">Категории</div>
<div class="select__drop js-select-drop">
<div class="select__option js-select-option{% if not category %} active{% endif %}"
data-category-option data-category-url="{% url 'courses' %}">
<div class="select__title">Все курсы</div>
<div class="select__option js-select-option{% if not category.0 %} active{% endif %}" data-category-option>
<div class="select__title">Все категории</div>
</div>
{% category_items category %}
</div>
<input class="select__input" type="hidden"></div>
</div>
</div>
<div class="head__field field">
<div class="field__wrap">
<div class="field__select select js-select{% if age.0 %} selected{% endif %}">
<div class="select__head js-select-head">{% if age_name %}{{ age_name }}{% else %}Возраст{% endif %}</div>
<div class="select__drop js-select-drop">
<div class="select__option js-select-option{% if not age.0 %} active{% endif %}" data-age-option>
<div class="select__title">Любой возраст</div>
</div>
{% for a in ages %}
<div class="select__option js-select-option{% if age and age.0 == a.0 %} active{% endif %}" data-age-option
data-age="{{ a.0 }}" data-age-name="{{ a.1 }}">
<div class="select__title">{{ a.1 }}</div>
</div>
{% endfor %}
</div>
<input class="select__input" type="hidden"></div>
</div>
</div>
</div>
</div>
<div class="courses">
@ -40,7 +57,7 @@
</div>
<div class="courses__load load">
{% if page_obj.has_next %}
<button class="load__btn btn" data-next-page-url="{% url 'courses' %}?page={{ page_obj.next_page_number }}">Подгрузить еще</button>
<button class="load__btn btn" data-next-page="{{ page_obj.next_page_number }}">Подгрузить еще</button>
{% endif %}
</div>
</div>

@ -1,6 +1,6 @@
{% for cat in category_items %}
<div class="select__option js-select-option{% if category == cat.id %} active{% endif %}"
data-category-option data-category-name="{{ cat.title }}" data-category-url="{% url 'courses' %}?category={{ cat.id }}">
<div class="select__option js-select-option{% if category == cat.id %} active{% endif %}" data-category-option
data-category-name="{{ cat.title }}" data-category="{{ cat.id }}">
<div class="select__title">{{ cat.title }}</div>
</div>
{% endfor %}
{% endfor %}

@ -31,15 +31,9 @@
</div>
<div class="lesson">
<div class="lesson__row">
<div class="lesson__preview">
<div class="lesson__pic-wrapper">
<img class="lesson__pic"
src="{% if lesson.cover %}{{ lesson.cover.image_thumbnail.url }}{% else %}{% static 'img/no_cover.png' %}{% endif %}">
</div>
</div>
<div>
<div class="lesson__subtitle subtitle">{{ lesson.title }}</div>
<div class="lesson__content">{{ lesson.short_description | linebreaks }}</div>
<div class="lesson__content">{{ lesson.short_description | safe | linebreaks }}</div>
<a href="{% url 'user' lesson.author.id %}">
<div class="lesson__user user">
{% if lesson.author.photo %}
@ -60,6 +54,12 @@
</div>
</a>
</div>
<div class="lesson__preview">
<div class="lesson__pic-wrapper">
<img class="lesson__pic"
src="{% if lesson.cover %}{{ lesson.cover.image_thumbnail.url }}{% else %}{% static 'img/no_cover.png' %}{% endif %}">
</div>
</div>
</div>
</div>
</div>

@ -167,9 +167,8 @@ class CourseEditView(TemplateView):
self.object = Course.objects.create(
author=request.user,
)
#TODO
#if (request.user != self.object.author and request.user.role < User.AUTHOR_ROLE) or request.user.role != User.ADMIN_ROLE:
# raise Http404
if request.user != self.object.author and request.user.role != User.ADMIN_ROLE:
raise Http404
return super().get(request)
def get_context_data(self):
@ -283,6 +282,14 @@ class CoursesView(ListView):
filtered = CourseFilter(self.request.GET)
context.update(filtered.data)
context['course_items'] = Course.shuffle(context.get('course_items'))
context['ages'] = Course.AGE_CHOICES[1:]
age = context.get('age')
if age and age[0]:
age = int(age[0])
context['age'] = [age]
context['age_name'] = dict(Course.AGE_CHOICES).get(age, '')
else:
context['age_name'] = ''
return context
def get_template_names(self):

@ -14,7 +14,7 @@
<div class="section__center center center_sm">
<div class="lesson">
<div class="lesson__subtitle subtitle">{{ livelesson.title }}</div>
<div class="lesson__content">{{ livelesson.short_description | linebreaks }}</div>
<div class="lesson__content">{{ livelesson.short_description | safe | linebreaks }}</div>
<div class="lesson__video video">
{% if livelesson.stream_index %}
<iframe class="lesson__video_frame" src="https://player.vimeo.com/video/{{ livelesson.stream_index }}?autoplay=1" frameborder="0" webkitallowfullscreen

@ -24,7 +24,7 @@
</div>
<div class="lessons__subtitle subtitle">{{ livelesson.date }} // {{ livelesson.title }}</div>
<div class="lessons__row">
<div class="lessons__content">{{ livelesson.short_description | linebreaks }}</div>
<div class="lessons__content">{{ livelesson.short_description | safe | linebreaks | truncatechars_html:800 }}</div>
</div>
</div>
{% endfor %}

@ -11,80 +11,56 @@
</div>
<div class="gallery">
<div class="gallery__grid">
<div
class="gallery__item gallery__item_lg"
style="background-image: url({% get_media_prefix %}instagram/results/0.jpg);"
>
<a href="{% get_media_prefix %}instagram/results/0.jpg"
class="gallery__item gallery__item_lg">
<img class="gallery__pic" src="{% get_media_prefix %}instagram/results/0.jpg" onerror="this.style.display='none'">
</div>
<div
class="gallery__item"
style="background-image: url({% get_media_prefix %}instagram/results/1.jpg);"
>
{% comment %} <img class="gallery__pic" src="{% get_media_prefix %}instagram/results/1.jpg" onerror="this.style.display='none'"> {% endcomment %}
</div>
<div
class="gallery__item"
style="background-image: url({% get_media_prefix %}instagram/results/2.jpg);"
>
{% comment %} <img class="gallery__pic" src="{% get_media_prefix %}instagram/results/2.jpg" onerror="this.style.display='none'"> {% endcomment %}
</div>
<div
class="gallery__item"
style="background-image: url({% get_media_prefix %}instagram/results/3.jpg);"
>
{% comment %} <img class="gallery__pic" src="{% get_media_prefix %}instagram/results/3.jpg" onerror="this.style.display='none'"> {% endcomment %}
</div>
<div
class="gallery__item"
style="background-image: url({% get_media_prefix %}instagram/results/4.jpg);"
>
{% comment %} <img class="gallery__pic" src="{% get_media_prefix %}instagram/results/4.jpg" onerror="this.style.display='none'"> {% endcomment %}
</div>
<div
class="gallery__item"
style="background-image: url({% get_media_prefix %}instagram/results/5.jpg);"
>
{% comment %} <img class="gallery__pic" src="{% get_media_prefix %}instagram/results/5.jpg" onerror="this.style.display='none'"> {% endcomment %}
</div>
</a>
<a href="{% get_media_prefix %}instagram/results/1.jpg"
class="gallery__item">
<img class="gallery__pic" src="{% get_media_prefix %}instagram/results/1.jpg" onerror="this.style.display='none'">
</a>
<a href="{% get_media_prefix %}instagram/results/2.jpg"
class="gallery__item">
<img class="gallery__pic" src="{% get_media_prefix %}instagram/results/2.jpg" onerror="this.style.display='none'">
</a>
<a href="{% get_media_prefix %}instagram/results/3.jpg"
class="gallery__item">
<img class="gallery__pic" src="{% get_media_prefix %}instagram/results/3.jpg" onerror="this.style.display='none'">
</a>
<a href="{% get_media_prefix %}instagram/results/4.jpg"
class="gallery__item">
<img class="gallery__pic" src="{% get_media_prefix %}instagram/results/4.jpg" onerror="this.style.display='none'">
</a>
<a href="{% get_media_prefix %}instagram/results/5.jpg"
class="gallery__item">
<img class="gallery__pic" src="{% get_media_prefix %}instagram/results/5.jpg" onerror="this.style.display='none'">
</a>
</div>
<div class="gallery__grid">
<div
class="gallery__item"
style="background-image: url({% get_media_prefix %}instagram/results/6.jpg);"
>
{% comment %} <img class="gallery__pic" src="{% get_media_prefix %}instagram/results/6.jpg" onerror="this.style.display='none'"> {% endcomment %}
</div>
<div
class="gallery__item"
style="background-image: url({% get_media_prefix %}instagram/results/7.jpg);"
>
{% comment %} <img class="gallery__pic" src="{% get_media_prefix %}instagram/results/7.jpg" onerror="this.style.display='none'"> {% endcomment %}
</div>
<div
class="gallery__item"
style="background-image: url({% get_media_prefix %}instagram/results/8.jpg);"
>
{% comment %} <img class="gallery__pic" src="{% get_media_prefix %}instagram/results/8.jpg" onerror="this.style.display='none'"> {% endcomment %}
</div>
<div class="gallery__item gallery__item_lg"
style="background-image: url({% get_media_prefix %}instagram/results/9.jpg);"
>
{% comment %} <img class="gallery__pic" src="{% get_media_prefix %}instagram/results/9.jpg" onerror="this.style.display='none'"> {% endcomment %}
</div>
<div
class="gallery__item"
style="background-image: url({% get_media_prefix %}instagram/results/10.jpg);"
>
{% comment %} <img class="gallery__pic" src="{% get_media_prefix %}instagram/results/10.jpg" onerror="this.style.display='none'"> {% endcomment %}
</div>
<div
class="gallery__item"
style="background-image: url({% get_media_prefix %}instagram/results/11.jpg);"
>
{% comment %} <img class="gallery__pic" src="{% get_media_prefix %}instagram/results/11.jpg" onerror="this.style.display='none'"> {% endcomment %}
</div>
<a href="{% get_media_prefix %}instagram/results/6.jpg"
class="gallery__item">
<img class="gallery__pic" src="{% get_media_prefix %}instagram/results/6.jpg" onerror="this.style.display='none'">
</a>
<a href="{% get_media_prefix %}instagram/results/7.jpg"
class="gallery__item">
<img class="gallery__pic" src="{% get_media_prefix %}instagram/results/7.jpg" onerror="this.style.display='none'">
</a>
<a href="{% get_media_prefix %}instagram/results/8.jpg"
class="gallery__item">
<img class="gallery__pic" src="{% get_media_prefix %}instagram/results/8.jpg" onerror="this.style.display='none'">
</a>
<a href="{% get_media_prefix %}instagram/results/9.jpg"
class="gallery__item gallery__item_lg">
<img class="gallery__pic" src="{% get_media_prefix %}instagram/results/9.jpg" onerror="this.style.display='none'">
</a>
<a href="{% get_media_prefix %}instagram/results/10.jpg"
class="gallery__item">
<img class="gallery__pic" src="{% get_media_prefix %}instagram/results/10.jpg" onerror="this.style.display='none'">
</a>
<a href="{% get_media_prefix %}instagram/results/11.jpg"
class="gallery__item">
<img class="gallery__pic" src="{% get_media_prefix %}instagram/results/11.jpg" onerror="this.style.display='none'">
</a>
</div>
</div>
</div>

@ -3,9 +3,8 @@
<div class="section__center center">
<div class="title title_center">Видео-курсы без расписания</div>
<div class="text text_courses">Если вам не совсем удобно заниматься с нами каждый день в нашей онлайн-школе, специально для вас мы
делаем отдельные уроки в записи, которые вы можете проходить когда вам будем удобно.</div>
<div class="head">
<div class="head__text">Учите и развивайте креативное мышление когда и где угодно</div>
делаем отдельные уроки в записи, которые вы можете проходить когда вам будем удобно.<br><br>
Учите и развивайте креативное мышление когда и где угодно
</div>
<div class="courses">
<div class="courses__list">

@ -18,6 +18,9 @@
role: +'{{ request.user.role }}',
photo: '{% if request.user.photo %}{{ request.user.photo.url }}{% else %}{% static 'img/user_default.jpg' %}{% endif %}',
},
components: {}
components: {},
urls: {
courses: "{% url 'courses' %}"
},
};
</script>

@ -6,7 +6,7 @@
<div class="lock">
<div class="lock__label">ЧТОБЫ ПРОДОЛЖИТЬ ПРОСМОТР</div>
<div class="lock__title title">{{ course.title }}</div>
<div class="lock__content">{{ course.short_description }}</div>
<div class="lock__content">{{ course.short_description | safe | linebreaks }}</div>
<a href="{% url 'course-checkout' course.id %}" class="lock__btn btn">КУПИТЬ КУРС</a>
</div>
</div>

@ -9,7 +9,7 @@
{% include "templates/blocks/about.html" %}
{% include "templates/blocks/last_courses.html" %}
{% include "templates/blocks/gallery.html" %}
{% include "templates/blocks/game.html" %}
{% include "templates/blocks/teachers.html" %}
{% include "templates/blocks/game.html" %}
{% include "templates/blocks/partners.html" %}
{% endblock content %}

@ -68,7 +68,7 @@ class IndexView(TemplateView):
'online': online,
'online_coming_soon': online_coming_soon,
'school_schedule': school_schedule,
'course_items': Course.shuffle(Course.objects.filter(status=Course.PUBLISHED)[:6]),
'course_items': Course.shuffle(Course.objects.filter(status=Course.PUBLISHED)[:3]),
'is_purchased': school_payment_exists,
'min_school_price': SchoolSchedule.objects.aggregate(Min('month_price'))['month_price__min'],
'school_schedules': SchoolSchedule.objects.all(),

@ -37,10 +37,8 @@
v-bind:class="{ error: ($v.course.short_description.$dirty || showErrors) && $v.course.short_description.$invalid }">
<div class="field__label">{{titles.shortDescription}}</div>
<div class="field__wrap">
<textarea class="field__textarea"
v-autosize="course.short_description"
@input="$v.course.short_description.$touch()"
v-model="course.short_description"></textarea>
<vue-redactor :value="course.short_description"
v-on:update:value="(value) => { this.course.short_description = value; }" />
</div>
</div>
</div>
@ -49,7 +47,7 @@
v-bind:class="{ error: ($v.course.category.$dirty || showErrors) && $v.course.category.$invalid }">
<div class="field__label field__label_gray">КАТЕГОРИЯ</div>
<div class="field__wrap">
<lil-select :value.sync="categorySelect" :options="categoryOptions"
<lil-select :value.sync="course.category" :options="categoryOptions"
placeholder="Выберите категорию"/>
</div>
</div>
@ -113,6 +111,13 @@
<button disabled class="field__append">руб.</button>
</div>
</div>
<div v-if="!live" class="info__field field">
<div class="field__label field__label_gray">ВОЗРАСТ</div>
<div class="field__wrap">
<lil-select :value.sync="course.age" :options="ages" value-key="value"
placeholder="Выберите возраст"/>
</div>
</div>
<label v-if="me && !live && me.role === ROLE_ADMIN" class="info__switch switch switch_lg">
<input type="checkbox" class="switch__input" v-model="course.is_featured">
<span class="switch__content">Выделить</span>
@ -198,7 +203,7 @@
</div>
<div class="lessons__subtitle subtitle">{{ lesson.title }}</div>
<div class="lessons__row">
<div class="lessons__content">{{ lesson.short_description }}</div>
<div class="lessons__content" v-html="lesson.short_description"></div>
</div>
</div>
</vue-draggable>
@ -232,6 +237,7 @@
import LinkInput from './inputs/LinkInput'
import DatePicker from 'vuejs-datepicker'
import BlockContent from './blocks/BlockContent'
import VueRedactor from './redactor/VueRedactor';
import LilSelect from "./inputs/LilSelect";
import LessonRedactor from "./LessonRedactor";
import {api} from "../js/modules/api";
@ -269,6 +275,7 @@
duration: null,
author: null,
price: null,
age: 0,
url: '',
coverImage: '',
kit__body: null,
@ -333,6 +340,40 @@
'value': '18:00',
}
],
ages: [
{
'title': 'Любой возраст',
'value': 0,
},
{
'title': 'до 5',
'value': 1,
},
{
'title': '5-7',
'value': 2,
},
{
'title': '7-9',
'value': 3,
},
{
'title': '9-12',
'value': 4,
},
{
'title': '12-15',
'value': 5,
},
{
'title': '15-18',
'value': 6,
},
{
'title': 'от 18',
'value': 7,
},
],
weekdays: [
'',
@ -441,15 +482,6 @@
this.course.url = slugify(this.course.title);
}
},
updateCategory() {
if (this.categoryOptions && Array.isArray(this.categoryOptions) && this.course.category) {
this.categoryOptions.forEach((category) => {
if (category.id === this.course.category) {
this.course.categorySelect = category;
}
});
}
},
removeLesson(lessonIndex) {
if (!confirm('Вы действительно хотите удалить этот урок?')) {
return;
@ -885,14 +917,8 @@
promises.push(cats);
cats.then((response) => {
if (response.data) {
this.categoryOptions = response.data.results.map((category) => {
return {
title: category.title,
value: category.id
}
});
this.categoryOptions = response.data.results;
}
this.updateCategory();
});
if(this.live) {
@ -932,7 +958,6 @@
this.scheduleOptions = _.orderBy(options, (item)=>{return moment(item.value)});
}
this.updateCategory();
});
}
@ -1003,23 +1028,6 @@
this.course.price = value || 0;
}
},
categorySelect: {
get() {
if (!this.categoryOptions || this.categoryOptions.length === 0 || !this.course || !this.course.category) {
return null;
}
let value;
this.categoryOptions.forEach((category) => {
if (category.value === this.course.category) {
value = category;
}
});
return value;
},
set(value) {
this.course.category = value.value;
}
},
// userSelect: {
// get() {
// if (!this.users || this.users.length === 0 || !this.course || !this.course.author) {
@ -1078,6 +1086,7 @@
'vue-datepicker': DatePicker,
'lesson-redactor': LessonRedactor,
'vue-draggable': Draggable,
'vue-redactor': VueRedactor,
}
}
</script>
@ -1178,9 +1187,10 @@
overflow: scroll;
}
.field_short_description {
.field_short_description .redactor-box {
overflow-x: visible;
overflow-y: auto;
max-height: 200px;
overflow: scroll;
}
</style>

@ -27,7 +27,8 @@
<div class="kit__field field"
v-bind:class="{ error: $v.currentLesson.short_description.$invalid }">
<div class="field__wrap">
<textarea class="field__input" v-autosize="lesson.short_description" placeholder="Описание урока" v-model="lesson.short_description"></textarea>
<vue-redactor :value="lesson.short_description"
v-on:update:value="(value) => { this.lesson.short_description = value; }" placeholder="Описание урока"/>
</div>
</div>
</div>
@ -46,6 +47,7 @@
<script>
import BlockContent from './blocks/BlockContent'
import LilImage from "./blocks/Image"
import VueRedactor from './redactor/VueRedactor';
import {api} from "../js/modules/api";
import Draggable from 'vuedraggable';
import _ from 'lodash'
@ -72,6 +74,7 @@
components: {
BlockContent,
'lil-image': LilImage,
'vue-redactor': VueRedactor,
}
}
</script>

@ -4,8 +4,9 @@
{{ selectedTitle }}
</div>
<div class="select__drop">
<div v-for="option in options" :class="{ select__option: true, active: value && option.value == value.value }" @click.stop.prevent="selectOption(option)">
<div class="select__title">{{ option.title }}</div>
<div v-for="option in options" :class="{ select__option: true, active: option[vk] == selectedValue }"
@click.stop.prevent="selectOption(option)">
<div class="select__title">{{ option[tk] }}</div>
</div>
</div>
</div>
@ -14,11 +15,14 @@
<script>
export default {
name: "lil-select",
props: ["options", "value", "valueKey", "placeholder"],
props: ["options", "value", "titleKey", "valueKey", "isObj", "placeholder"],
data() {
return {
key: this.valueKey ? this.valueKey : 'title',
tk: this.titleKey || 'title',
vk: this.valueKey || 'id',
isOpened: false,
selected: null,
optionsDict: {},
}
},
methods: {
@ -30,22 +34,30 @@
},
selectOption(option) {
this.isOpened = !this.isOpened;
this.$emit('update:value', option);
this.$emit('update:value', this.isObj ? option : option[this.vk]);
this.selected = option;
}
},
mounted() {
document.addEventListener("click", this.clickListener);
this.options.forEach((option) => {
this.optionsDict[option[this.vk]] = option;
});
this.selected = this.optionsDict[this.selectedValue];
},
destroyed() {
document.removeEventListener("click", this.clickListener);
},
computed: {
isSelected() {
return this.value && this.value[this.key] ? true : false;
return !!this.selected;
},
selectedValue() {
return this.isObj ? this.value && this.value[this.vk] : this.value;
},
selectedTitle() {
if (this.isSelected) {
return this.value[this.key];
return this.selected[this.tk];
}
return this.placeholder ? this.placeholder : '';
}
@ -55,4 +67,4 @@
<style scoped>
</style>
</style>

@ -7,7 +7,7 @@
import './redactor-full.js';
export default {
props: ['value', 'placeholder'],
props: ['value', 'placeholder', 'air', 'buttonsHide'],
name: "vue-redactor",
data() {
return {
@ -18,7 +18,7 @@
const me = this;
$(me.$refs.input).redactor({
air: true,
buttonsHide: ['image', 'format'],
buttonsHide: this.buttonsHide || ['image', 'format'],
lang: 'ru',
placeholder: this.placeholder,
callbacks: {

@ -108,6 +108,7 @@ export const api = {
short_description: courseObject.short_description,
category: courseObject.category,
price: courseObject.is_paid && courseObject.price || 0,
age: courseObject.age,
deferred_start_at: deferredStart,
duration: courseObject.duration || 0,
is_featured: courseObject.is_featured,

@ -12,9 +12,12 @@ moment.locale('ru');
const history = createHistory();
$(document).ready(function () {
const currentCategory = $('div.js-select-option.active[data-category-option]').attr('data-category-name');
if(currentCategory) {
$('.js-select[data-category-select] .js-select-head').text(currentCategory);
const categoryName = $('div.js-select-option.active[data-category-option]').attr('data-category-name');
let category = $('div.js-select-option.active[data-category-option]').attr('data-category');
let age = $('div.js-select-option.active[data-age-option]').data('age');
let page = 1;
if(category) {
$('.js-select[data-category-select] .js-select-head').text(categoryName);
}
// Обработчик отложенных курсов
setInterval(() => {
@ -29,17 +32,30 @@ $(document).ready(function () {
// Обработчик кнопки "Подгрузить еще"
$('.courses').on('click', 'button.load__btn', function () {
load_courses($(this).attr('data-next-page-url'), false);
page = $(this).attr('data-next-page');
loadCourses();
});
// Обработчик выбора категории
$('div.js-select-option[data-category-option]').on('click', function (e) {
e.preventDefault();
const currentCategory = $(this).attr('data-category-name');
const categoryName = $(this).attr('data-category-name');
$('[data-category-name]').removeClass('active');
$(`[data-category-name='${currentCategory}']`).addClass('active');
history.replace($(this).attr('data-category-url'));
load_courses($(this).attr('data-category-url'), true);
$(`[data-category-name='${categoryName}']`).addClass('active');
category = $(this).attr('data-category');
page = 1;
loadCourses(true);
});
// Обработчик выбора возраста
$('div.js-select-option[data-age-option]').on('click', function (e) {
e.preventDefault();
const currentAge = $(this).attr('data-age-name');
$('[data-age-name]').removeClass('active');
$(`[data-age-name='${currentAge}']`).addClass('active');
age = $(this).attr('data-age');
page = 1;
loadCourses(true);
});
// Обработчик лайков
@ -95,42 +111,50 @@ $(document).ready(function () {
}
});
})
});
function load_courses(coursesUrl, fromStart) {
$('.courses__list').css('opacity', '0.9');
const buttonElement = $('.courses').find('button.load__btn');
if (!fromStart) {
buttonElement.addClass('loading');
}
$.ajax(coursesUrl, {
method: 'GET'
})
.done(function (data) {
if (data.success === true) {
if (!fromStart) {
$('.courses__list').append(data.content);
} else {
$('.courses__list').html(data.content);
function loadCourses(replaceHistory) {
$('.courses__list').css('opacity', '0.9');
const buttonElement = $('.courses').find('button.load__btn');
let coursesUrl = window.LIL_STORE.urls.courses + '?' + $.param({
category: category,
age: age,
});
if (page > 1) {
buttonElement.addClass('loading');
}
else{
history.replace(coursesUrl);
}
coursesUrl += `&page=${page}`;
$.ajax(coursesUrl, {
method: 'GET'
})
.done(function (data) {
if (data.success === true) {
if (page > 1) {
$('.courses__list').append(data.content);
} else {
$('.courses__list').html(data.content);
}
if (data.next_url) {
buttonElement.attr('data-next-page-url', data.next_url);
buttonElement.show();
} else {
buttonElement.hide()
}
}
if (data.next_url) {
buttonElement.attr('data-next-page-url', data.next_url);
buttonElement.show();
} else {
buttonElement.hide()
})
.fail(function (xhr) {
if (xhr.status === 404) {
// Нет результатов, скрываем кнопку
buttonElement.hide();
}
}
})
.fail(function (xhr) {
if (xhr.status === 404) {
// Нет результатов, скрываем кнопку
buttonElement.hide();
}
})
.always(function () {
$('.courses__list').css('opacity', '1');
if (buttonElement) {
buttonElement.removeClass('loading');
}
});
}
})
.always(function () {
$('.courses__list').css('opacity', '1');
if (buttonElement) {
buttonElement.removeClass('loading');
}
});
}
});

@ -891,11 +891,11 @@ a[name]
font-size: 20px
.section
padding: 60px 0
padding: 50px 0
+t
padding: 40px 0
+m
padding: 30px 0
+m
padding: 20px 0
&_review
background: url(../img/bg-elephants.jpg) 0 0 / 100px 102px
&_gray
@ -1352,6 +1352,8 @@ a[name]
&__pic
display: block
width: 100%
height: 100%
object-fit: cover
.app
position: relative
@ -2456,11 +2458,11 @@ a.grey-link
.go
display: flex
margin-bottom: 50px
margin-bottom: 40px
justify-content: space-between
align-items: center
+t
margin-bottom: 30px
margin-bottom: 20px
&__item
display: flex
max-width: calc(50% - 20px)
@ -2517,11 +2519,11 @@ a.grey-link
fill: white
.course
margin-bottom: 60px
margin-bottom: 50px
&__head
display: flex
min-height: 40px
margin-bottom: 30px
margin-bottom: 20px
align-items: center
justify-content: space-between
+t
@ -2531,9 +2533,9 @@ a.grey-link
+t
margin-bottom: 10px
&__content
margin-bottom: 40px
margin-bottom: 30px
+t
margin-bottom: 25px
margin-bottom: 15px
&__about
position: relative
margin-bottom: 40px
@ -2563,9 +2565,9 @@ a.grey-link
&:active
color: rgba(white,.4)
&__user
margin-bottom: 40px
margin-bottom: 30px
+t
margin-bottom: 30px
margin-bottom: 20px
&__info
display: flex
align-items: center
@ -2580,17 +2582,17 @@ a.grey-link
margin: 20px 0 0
&__metas
display: flex
margin-bottom: 45px
margin-bottom: 35px
justify-content: space-between
+m
display: block
margin-bottom: 25px
margin-bottom: 15px
&__metas &__meta
+m
margin-bottom: 20px
&__actions
display: flex
margin: 0 -10px 50px
margin: 0 -10px 40px
&__action
position: relative
margin: 0 10px
@ -2658,6 +2660,8 @@ a.grey-link
visibility: visible
&__subtitle
margin-bottom: 20px
text-transform: none
letter-spacing: normal
&__row
display: flex
+m
@ -2676,6 +2680,8 @@ a.grey-link
object-fit: cover
width: 100%
height: 100%
&__content-wrapper
flex: 1
&__content
flex: 0 0 calc(100% - 165px)
&__actions
@ -2715,6 +2721,8 @@ a.grey-link
.lesson
&__subtitle
letter-spacing: normal
text-transform: none
margin-bottom: 10px
color: #191919
&__content
@ -4187,6 +4195,8 @@ a
font-size: 16px
padding-bottom: 24px
text-align: left
margin: 0
max-width: 100%
&.pic
position: relative
width: 100%

Loading…
Cancel
Save