diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9a2b0562..1d78b5d1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,16 +1,68 @@ stages: - - deploy + - deploy + - db + - stop + +variables: + REVIEW_DOMAIN: back-review.lil.school deploy_prod: stage: deploy script: - rsync -a --stats --delete --exclude="docker/data/" --exclude="docker/.env" ./ /work/www/lil.school/ - cd /work/www/lil.school/docker/ - - docker-compose -f docker-compose-prod.yml up --build -d + - docker-compose -f docker-compose-prod.yml -p back up --build -d environment: - name: prod/site + name: prod url: https://lil.school only: - master tags: - prod + +deploy_review: + stage: deploy + script: + - export REVIEW_HOST=$CI_COMMIT_REF_SLUG-$REVIEW_DOMAIN + - cd docker + - cp .env.review .env + - docker-compose -f docker-compose-review.yml -p back$CI_COMMIT_REF_NAME up --build -d + environment: + name: review/$CI_COMMIT_REF_SLUG + url: https://$CI_COMMIT_REF_SLUG-$REVIEW_DOMAIN + on_stop: stop-review + tags: + - review + only: + - branches + +stop-review: + stage: stop + environment: + name: review/$CI_COMMIT_REF_SLUG + action: stop + script: + - export REVIEW_HOST=$CI_COMMIT_REF_SLUG-$REVIEW_DOMAIN + - cd docker + - docker-compose -f docker-compose-review.yml -p back$CI_COMMIT_REF_NAME down + - rm -rf /work/data/back_${CI_COMMIT_REF_NAME}/ + when: manual + only: + - branches + tags: + - review + +prod-db: + stage: db + script: + - export REVIEW_HOST=$CI_COMMIT_REF_SLUG-$REVIEW_DOMAIN + - cd docker + - cp .env.review .env + - docker-compose -f docker-compose-review.yml -p back$CI_COMMIT_REF_NAME restart db + - echo 'DROP DATABASE IF EXISTS lilcity; CREATE DATABASE lilcity' | docker-compose -f docker-compose-review.yml -p back$CI_COMMIT_REF_NAME exec -T -u postgres db psql postgres + - /work/scripts/get_prod_db.sh | docker-compose -f docker-compose-review.yml -p back$CI_COMMIT_REF_NAME exec -T -u postgres db psql lilcity + when: manual + only: + - branches + tags: + - review diff --git a/api/v1/permissions.py b/api/v1/permissions.py index 95a136ee..c0854a4f 100644 --- a/api/v1/permissions.py +++ b/api/v1/permissions.py @@ -15,7 +15,7 @@ class IsAdmin(BasePermission): class IsTeacherOrAdmin(BasePermission): def has_permission(self, request, view): return request.user.is_authenticated and ( - request.user.role > User.TEACHER_ROLE or + request.user.role >= User.TEACHER_ROLE or request.user.is_staff or request.user.is_superuser ) diff --git a/api/v1/serializers/course.py b/api/v1/serializers/course.py index d1e8dcba..bdb1a289 100644 --- a/api/v1/serializers/course.py +++ b/api/v1/serializers/course.py @@ -184,6 +184,7 @@ class LessonCreateSerializer(DispatchContentMixin, serializers.ModelSerializer): 'created_at', 'update_at', 'deactivated_at', + 'position', ) read_only_fields = ( @@ -196,6 +197,9 @@ class LessonCreateSerializer(DispatchContentMixin, serializers.ModelSerializer): def create(self, validated_data): content = validated_data.pop('content', []) lesson = super().create(validated_data) + if not validated_data.get('position'): + lesson.set_last_position() + lesson.save() self.dispatch_content(lesson, content) return lesson @@ -226,6 +230,7 @@ class LessonSerializer(serializers.ModelSerializer): 'created_at', 'update_at', 'deactivated_at', + 'position', ) read_only_fields = ( diff --git a/apps/course/migrations/0039_lesson_position.py b/apps/course/migrations/0039_lesson_position.py new file mode 100644 index 00000000..7a26d8e5 --- /dev/null +++ b/apps/course/migrations/0039_lesson_position.py @@ -0,0 +1,18 @@ +# Generated by Django 2.0.6 on 2018-07-02 13:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('course', '0038_lesson_author'), + ] + + operations = [ + migrations.AddField( + model_name='lesson', + name='position', + field=models.PositiveSmallIntegerField(default=1, verbose_name='Положение на странице'), + ), + ] diff --git a/apps/course/models.py b/apps/course/models.py index 7d0300c7..942a0593 100644 --- a/apps/course/models.py +++ b/apps/course/models.py @@ -177,10 +177,18 @@ class Lesson(BaseModel, DeactivatedMixin): ) created_at = models.DateTimeField(auto_now_add=True) update_at = models.DateTimeField(auto_now=True) + position = models.PositiveSmallIntegerField( + 'Положение на странице', + default=1, + ) def __str__(self): return self.title + def set_last_position(self): + if self.course: + self.position = self.course.lessons.count() + def save(self, *args, **kwargs): if not self.author and self.course and self.course.author: self.author = self.course.author diff --git a/apps/course/templates/course/course_only_lessons.html b/apps/course/templates/course/course_only_lessons.html index a919e08f..f0217bcf 100644 --- a/apps/course/templates/course/course_only_lessons.html +++ b/apps/course/templates/course/course_only_lessons.html @@ -173,7 +173,7 @@
Содержание курса
- {% for lesson in course.lessons.all %} + {% for lesson in lessons %} {% if course.author == request.user or request.user.role >= request.user.TEACHER_ROLE or paid %} {% else %} diff --git a/apps/course/views.py b/apps/course/views.py index 8a54df71..b4fd6fd1 100644 --- a/apps/course/views.py +++ b/apps/course/views.py @@ -203,6 +203,8 @@ class CourseView(DetailView): status=Pingback.PINGBACK_TYPE_RISK_UNDER_REVIEW, ).exists() context['only_lessons'] = self.only_lessons + if self.only_lessons: + context['lessons'] = self.object.lessons.order_by('position') return context def get_queryset(self): diff --git a/apps/payment/views.py b/apps/payment/views.py index 4f4a958a..2f70df36 100644 --- a/apps/payment/views.py +++ b/apps/payment/views.py @@ -222,7 +222,9 @@ class PaymentwallCallbackView(View): date_start = arrow.get(school_payment.date_end).shift(days=1).datetime date_end = arrow.get(date_start).shift(months=1).datetime else: - month = 0 if now().day >= 1 and now().day <= 10 else 1 + #month = 0 if now().day >= 1 and now().day <= 10 else 1 + # Логика июльского лагеря: до конца июля приобретаем только на текущий месяц + month = 0 date_start = self.add_months(sourcedate=now().replace(hour=0, minute=0, day=1), months=month) date_end = arrow.get(date_start).shift(months=1, minutes=-1).datetime payment.date_start = date_start diff --git a/apps/school/templates/blocks/online.html b/apps/school/templates/blocks/online.html index 1da3e731..c4c82931 100644 --- a/apps/school/templates/blocks/online.html +++ b/apps/school/templates/blocks/online.html @@ -3,7 +3,7 @@
ПРЯМОЙ ЭФИР
Каждый день в 17.00 (по Мск)
-
Кроме выходных. Запись эфира доступна в течение 24-х часов.
+
Кроме выходных. Запись эфира доступна по завершению трансляции
diff --git a/apps/school/templates/summer/_schedule_purchased_item.html b/apps/school/templates/summer/_schedule_purchased_item.html index b9d1db1f..0e86ccba 100644 --- a/apps/school/templates/summer/_schedule_purchased_item.html +++ b/apps/school/templates/summer/_schedule_purchased_item.html @@ -1,6 +1,6 @@ {% load static %} {% load thumbnail %}
-
+
{{ school_schedule }} @@ -8,16 +8,16 @@ {% if live_lesson %}
{{ live_lesson.date }}
{% endif %} -
-
-
{{ school_schedule.start_at }} (МСК)
- {% if school_schedule.weekday in school_schedules_purchased %} - {% if live_lesson and live_lesson.title %} - {% include './open_lesson.html' %} +
+
{{ school_schedule.start_at }} (МСК)
+ {% if school_schedule.weekday in school_schedules_purchased %} + {% if live_lesson and live_lesson.title %} + {% include './open_lesson.html' %} + {% endif %} + {% else %} + {% include './day_pay_btn.html' %} {% endif %} - {% else %} - {% include './day_pay_btn.html' %} - {% endif %} +
{% comment %} @@ -26,53 +26,55 @@ {% endif %}
-
-
- {% thumbnail live_lesson.cover.image "70x70" crop="center" as im %} - - {% empty %} - - {% endthumbnail %} +
+
+
+ {% thumbnail live_lesson.cover.image "70x70" crop="center" as im %} + + {% empty %} + + {% endthumbnail %} +
-
-
-
{{ school_schedule.title }}{% if live_lesson and live_lesson.title %}, - {{ live_lesson.title }} + diff --git a/apps/school/templates/summer/open_lesson.html b/apps/school/templates/summer/open_lesson.html index 46999c7f..abb8a9a2 100644 --- a/apps/school/templates/summer/open_lesson.html +++ b/apps/school/templates/summer/open_lesson.html @@ -1,4 +1,4 @@ смотреть урок +>подробнее diff --git a/apps/user/templates/user/profile.html b/apps/user/templates/user/profile.html index caddc204..6b0f0b07 100644 --- a/apps/user/templates/user/profile.html +++ b/apps/user/templates/user/profile.html @@ -62,22 +62,22 @@
-{% if not guest %}
+ {% if owner %} - {% if not simple_user %} - - {% endif %}
+ {% if owner %}
{% if is_purchased_future %}
@@ -126,8 +126,8 @@
- {% if not simple_user %} -
+ {% endif %} +
{% if published.exists %} @@ -145,10 +145,8 @@
- {% endif %}
-{% endif %} {% endblock content %} diff --git a/apps/user/views.py b/apps/user/views.py index 14ecef7b..500d8404 100644 --- a/apps/user/views.py +++ b/apps/user/views.py @@ -56,6 +56,7 @@ class UserView(DetailView): else: context['simple_user'] = True context['guest'] = True + context['owner'] = self.request.user.id == self.object.id if context['guest'] and self.object.role <= User.USER_ROLE: raise Http404() diff --git a/docker/.env.review b/docker/.env.review new file mode 100644 index 00000000..351dc795 --- /dev/null +++ b/docker/.env.review @@ -0,0 +1,22 @@ +DEBUG=True +ALLOWED_HOSTS=* +PORT=8000 +CORS_ORIGIN_WHITELIST=lilcity.9ev.ru:8080 +LANG=ru_RU.UTF-8 +POSTGRES_DB=lilcity +POSTGRES_USER=lilcity +POSTGRES_PASSWORD=GPVs/E/{5&qe +DJANGO_SETTINGS_MODULE=project.settings +DATABASE_SERVICE_HOST=db +SECRET_KEY=jelm*91lj(_-o20+6^a+bgv!4s6e_efry^#+f#=1ak&s1xr-2j +MAILGUN_API_KEY=key-ec6af2d43d031d59bff6b1c8fb9390cb +MAILGUN_SENDER_DOMAIN=mail.9ev.ru +DEFAULT_FROM_EMAIL=postmaster@mail.9ev.ru +TWILIO_ACCOUNT=ACdf4a96b776cc764bc3ec0f0e136ba550 +TWILIO_TOKEN=559a6b1fce121759c9af2dcbb3f755ea +TWILIO_FROM_PHONE=+37128914409 +PAYMENTWALL_APP_KEY=d6f02b90cf6b16220932f4037578aff7 +PAYMENTWALL_SECRET_KEY=4ea515bf94e34cf28646c2e12a7b8707 +MIXPANEL_TOKEN=79bd6bfd98667ed977737e6810b8abcd +RAVEN_DSN=https://b545dac0ae0545a1bcfc443326fe5850:6f9c900cef7f4c11b63561030b37d15c@sentry.io/1197254 +ROISTAT_COUNTER_ID=09db30c750035ae3d70a41d5f10d59ec diff --git a/docker/Dockerfile b/docker/Dockerfile index 46722a2f..acaa74c8 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -18,4 +18,5 @@ ADD . /app/ COPY --from=front /web/build/ /app/web/build/ RUN python manage.py collectstatic --no-input RUN rm -rf /etc/nginx/ && cp -r docker/conf/nginx /etc/ && cp -r docker/conf/supervisor/* /etc/supervisor/conf.d/ && chown -R www-data:www-data /app/ +EXPOSE 80 ENTRYPOINT ["/app/docker/entrypoint_app.sh"] diff --git a/docker/conf/nginx/sites-available/default b/docker/conf/nginx/conf.d/default.conf similarity index 71% rename from docker/conf/nginx/sites-available/default rename to docker/conf/nginx/conf.d/default.conf index 1aaa7519..b20174f6 100644 --- a/docker/conf/nginx/sites-available/default +++ b/docker/conf/nginx/conf.d/default.conf @@ -19,9 +19,19 @@ server { location /media/instagram/results/ { expires 1d; + try_files $uri @prod; } + try_files $uri @prod; } + location @prod { + if ($host = "lil.school") { + return 404; + } + proxy_pass https://lil.school; + proxy_buffering off; + } + location / { try_files $uri @django; } diff --git a/docker/conf/nginx/nginx.conf b/docker/conf/nginx/nginx.conf index c477630e..2889f7ad 100644 --- a/docker/conf/nginx/nginx.conf +++ b/docker/conf/nginx/nginx.conf @@ -26,7 +26,6 @@ http { gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; include /etc/nginx/conf.d/*.conf; - include /etc/nginx/sites-enabled/*; set_real_ip_from 192.168.0.0/24; } diff --git a/docker/conf/nginx/sites-enabled/default b/docker/conf/nginx/sites-enabled/default deleted file mode 120000 index ad35b834..00000000 --- a/docker/conf/nginx/sites-enabled/default +++ /dev/null @@ -1 +0,0 @@ -/etc/nginx/sites-available/default \ No newline at end of file diff --git a/docker/docker-compose-prod.yml b/docker/docker-compose-prod.yml index 440f2cbf..c3b97c9e 100644 --- a/docker/docker-compose-prod.yml +++ b/docker/docker-compose-prod.yml @@ -10,6 +10,11 @@ services: - .env volumes: - ./data/postgres:/var/lib/postgresql/data + logging: &logging + driver: "json-file" + options: + max-size: "1m" + max-file: "1" redis: image: redis:4.0.9-alpine @@ -18,6 +23,7 @@ services: - "127.0.0.1:6379:6379" volumes: - ./data/redis:/data + logging: *logging app: build: @@ -36,3 +42,4 @@ services: links: - db - redis + logging: *logging diff --git a/docker/docker-compose-review.yml b/docker/docker-compose-review.yml new file mode 100644 index 00000000..9413f978 --- /dev/null +++ b/docker/docker-compose-review.yml @@ -0,0 +1,58 @@ +version: '3' + +services: + db: + image: postgres:10.3-alpine + restart: always + env_file: + - .env + volumes: + - /work/data/back_${CI_COMMIT_REF_NAME}/postgres:/var/lib/postgresql/data + logging: &logging + driver: "json-file" + options: + max-size: "1m" + max-file: "1" + networks: + - internal + - review + labels: + - traefik.enable=false + + redis: + image: redis:4.0.9-alpine + restart: always + volumes: + - /work/data/back_${CI_COMMIT_REF_NAME}/redis:/data + logging: *logging + networks: + - internal + - review + labels: + - traefik.enable=false + + app: + build: + context: ../ + dockerfile: docker/Dockerfile + restart: always + env_file: + - .env + volumes: + - /work/data/back_${CI_COMMIT_REF_NAME}/media:/app/media + depends_on: + - db + - redis + logging: *logging + networks: + - internal + - review + labels: + - traefik.frontend.rule=Host:${REVIEW_HOST} + - traefik.docker.network=review + +networks: + internal: + review: + external: + name: review diff --git a/docker/entrypoint_app.sh b/docker/entrypoint_app.sh index a61879d2..a346f89c 100755 --- a/docker/entrypoint_app.sh +++ b/docker/entrypoint_app.sh @@ -1,5 +1,6 @@ #!/bin/sh cd /app +chown www-data:www-data /app/media python manage.py migrate #python manage.py loaddata /app/apps/*/fixtures/*.json python2.7 /usr/bin/supervisord -n diff --git a/project/templates/blocks/popup_buy.html b/project/templates/blocks/popup_buy.html index f35c3474..96b3b29d 100644 --- a/project/templates/blocks/popup_buy.html +++ b/project/templates/blocks/popup_buy.html @@ -37,7 +37,7 @@ {% comment %} dont delete {% endcomment %} {{ school_schedule.title }} - {{school_schedule.month_price}}р + {{school_schedule.month_price}}р в мес. {% endfor %} @@ -58,7 +58,7 @@ {% comment %} dont delete {% endcomment %} {{ school_schedule.title }} - {{school_schedule.month_price}}р + {{school_schedule.month_price}}р в мес. {% endfor %} diff --git a/project/templates/blocks/promo.html b/project/templates/blocks/promo.html index 29cdd2b6..df3253e9 100644 --- a/project/templates/blocks/promo.html +++ b/project/templates/blocks/promo.html @@ -13,7 +13,7 @@
{% if user.is_authenticated and online %}
- Сейчас идёт прямой эфир урока «{{ school_schedule.title }}» + Сейчас идёт прямой эфир урока «{{ school_schedule.title }}, {{ school_schedule.current_live_lesson.title }}»
{% elif user.is_authenticated and online_coming_soon and school_schedule and school_schedule.start_at_humanize %} -
- Урок «{{ school_schedule.title }}» начнётся +
+ Урок «{{ school_schedule.title }}, {{ school_schedule.current_live_lesson.title }}» начнётся
{{ school_schedule.start_at_humanize }} @@ -46,23 +46,20 @@
{% else %}
- Урок Рисовальный лагерь, Альбрехт Дюрер завершен
- Следующий урок пройдет 4 июля + Присоединяйтесь в Рисовальный лагерь
- {% if is_purchased %} - Перейти в урок - {% else %} - {% if not is_purchased and not is_purchased_future %}Получить доступ{% endif %} {% if is_purchased_future and not is_purchased %}ваша подписка начинается {{school_purchased_future.date_start}}{% endif %} diff --git a/project/templates/lilcity/index.html b/project/templates/lilcity/index.html index a4d0539d..5d7b9d2d 100644 --- a/project/templates/lilcity/index.html +++ b/project/templates/lilcity/index.html @@ -25,7 +25,7 @@ {% comment %} {% endcomment %} - + @@ -76,7 +76,44 @@ &noscript=1"/> - + + + + + + + {% include "templates/blocks/mixpanel.html" %} diff --git a/web/src/components/CourseRedactor.vue b/web/src/components/CourseRedactor.vue index 3ba9359e..f4291156 100644 --- a/web/src/components/CourseRedactor.vue +++ b/web/src/components/CourseRedactor.vue @@ -215,24 +215,31 @@
Содержание курса
-
-
- - -
-
{{ lesson.title }}
-
-
{{ lesson.short_description }}
-
-
+ +
+
+ + + +
+
{{ lesson.title }}
+
+
{{ lesson.short_description }}
+
+
+
Загрузка...
@@ -307,7 +314,7 @@ price: null, url: '', coverImage: '', - coverImageId: null, + kit__body: null, is_paid: false, is_featured: true, is_deferred: false, @@ -507,6 +514,7 @@ api.removeCourseLesson(lesson.id, this.accessToken); } this.lessons.splice(lessonIndex, 1); + this.onLessonsChanged(); }, editLesson(lessonIndex) { this.currentLesson = this.lessons[lessonIndex]; @@ -581,11 +589,7 @@ }); } - document.getElementById('course-redactor__saving-status').innerText = 'СОХРАНЕНО'; - this.savingTimeout = setTimeout(() => { - document.getElementById('course-redactor__saving-status').innerText = ''; - }, 2000); - + this.changeSavingStatus(true); showNotification("success", 'Урок сохранён'); // this.goToLessons(); @@ -594,10 +598,7 @@ .catch((err) => { this.lessonSaving = false; //console.error(err); - document.getElementById('course-redactor__saving-status').innerText = 'ОШИБКА'; - this.savingTimeout = setTimeout(() => { - document.getElementById('course-redactor__saving-status').innerText = ''; - }, 2000); + this.changeSavingStatus(true, true); // alert('Произошло что-то страшное: '+err.toString()); console.log(err); if(err.response) { @@ -619,6 +620,14 @@ $(window).scrollTop(elementTop); }); }, + processCourseJson(data) { + this.course = api.convertCourseJson(data); + this.course.live = this.live; + this.lessons = data.lessons.map((lessonJson) => { + return api.convertLessonJson(lessonJson); + }); + this.course.duration = this.course.duration || ''; + }, loadCourseDraft() { //console.log('loadCourseDraft'); if(this.live) { return; } @@ -628,12 +637,8 @@ response .then((response) => { - this.course = api.convertCourseJson(response.data); - this.course.live = this.live; + this.processCourseJson(response.data); this.courseLoading = false; - this.lessons = response.data.lessons.map((lessonJson) => { - return api.convertLessonJson(lessonJson); - }); }) .catch((err) => { this.courseLoading = false; @@ -653,20 +658,18 @@ } request .then((response) => { - this.course = api.convertCourseJson(response.data); - this.course.live = this.live; - - /* if (this.live && this.course.date) { - this.course.date = _.find(this.scheduleOptions, (item) => { - return item.value == this.course.date; - }) - } */ - + this.processCourseJson(response.data); this.$nextTick(() => { this.courseLoading = false; }); - this.lessons = response.data.lessons.map((lessonJson) => { - return api.convertLessonJson(lessonJson); + this.lessons.sort((a, b) => { + if (a.position > b.position) { + return 1; + } + if (a.position < b.position) { + return -1; + } + return 0; }); }) .catch((err) => { @@ -794,17 +797,13 @@ } this.courseSaving = true; - clearTimeout(this.savingTimeout); - document.getElementById('course-redactor__saving-status').innerText = 'СОХРАНЕНИЕ...'; + this.changeSavingStatus(); const courseObject = this.course; courseObject.url = (courseObject.url) ? slugify(courseObject.url):courseObject.url; api.saveCourse(courseObject, this.accessToken) .then((response) => { this.courseSaving = false; - document.getElementById('course-redactor__saving-status').innerText = 'СОХРАНЕНО'; - this.savingTimeout = setTimeout(() => { - document.getElementById('course-redactor__saving-status').innerText = ''; - }, 2000); + this.changeSavingStatus(true); this.courseSyncHook = true; const courseData = api.convertCourseJson(response.data); if (this.course.coverImage) { @@ -854,10 +853,7 @@ this.courseSyncHook = false; this.courseSaving = false; //console.error(err); - document.getElementById('course-redactor__saving-status').innerText = 'ОШИБКА'; - this.savingTimeout = setTimeout(() => { - document.getElementById('course-redactor__saving-status').innerText = ''; - }, 2000); + this.changeSavingStatus(true, true); // alert('Произошло что-то страшное: '+err.toString()); //console.log(err.response.data); if(err.response) { @@ -886,10 +882,42 @@ this.viewSection = 'lessons-edit'; } }, + onLessonsChanged() { + let promises = []; + this.courseSaving = true; + this.lessons.map((lesson, index) => { + lesson.position = index + 1; + lesson.course_id = this.course.id; + let res = api.saveLesson(lesson, this.accessToken); + promises.push(res); + }); + Promise.all(promises).then(() => { + this.courseSaving = false; + this.changeSavingStatus(true); + }, () => { + this.courseSaving = false; + this.changeSavingStatus(true, true); + }); + }, pluralize(count, words) { var cases = [2, 0, 1, 1, 1, 2]; return words[ (count % 100 > 4 && count % 100 < 20) ? 2 : cases[ Math.min(count % 10, 5)] ]; - } + }, + changeSavingStatus(saved, error) { + let text = ''; + if(error) { + text = 'ОШИБКА'; + } else { + text = saved ? 'СОХРАНЕНО' : 'СОХРАНЕНИЕ...'; + } + clearTimeout(this.savingTimeout); + document.getElementById('course-redactor__saving-status').innerText = text; + if(saved || error){ + this.savingTimeout = setTimeout(() => { + document.getElementById('course-redactor__saving-status').innerText = ''; + }, 2000); + } + }, }, mounted() { this.mounting = true; @@ -1025,7 +1053,7 @@ }, displayPrice: { get: function () { - return this.course.is_paid ? (this.course.price || 0) : 0; + return this.course.is_paid ? (this.course.price || '') : ''; }, set: function (value) { this.course.price = value || 0; diff --git a/web/src/js/modules/api.js b/web/src/js/modules/api.js index 36dc5f99..990ac7b2 100644 --- a/web/src/js/modules/api.js +++ b/web/src/js/modules/api.js @@ -107,9 +107,9 @@ export const api = { author: courseObject.author ? courseObject.author : null, short_description: courseObject.short_description, category: courseObject.category, - price: courseObject.is_paid ? courseObject.price : 0, + price: courseObject.is_paid && courseObject.price || 0, deferred_start_at: deferredStart, - duration: courseObject.duration, + duration: courseObject.duration || 0, is_featured: courseObject.is_featured, slug: courseObject.url, date: (courseObject.date) ? courseObject.date.value:null, @@ -208,6 +208,7 @@ export const api = { title: lessonObject.title, short_description: lessonObject.short_description, course: lessonObject.course_id, + position: lessonObject.position, content: lessonObject.content.map((block, index) => { if (block.type === 'text') { return { @@ -287,7 +288,8 @@ export const api = { short_description: lessonJSON.short_description, coverImageId: lessonJSON.cover && lessonJSON.cover.id ? lessonJSON.cover.id : null, coverImage: lessonJSON.cover && lessonJSON.cover.image ? lessonJSON.cover.image : null, - content: api.convertContentResponse(lessonJSON.content) + content: api.convertContentResponse(lessonJSON.content), + position: lessonJSON.position, } }, convertCourseJson: (courseJSON) => { diff --git a/web/src/sass/_common.sass b/web/src/sass/_common.sass index 7e5a870a..8386a037 100755 --- a/web/src/sass/_common.sass +++ b/web/src/sass/_common.sass @@ -1832,17 +1832,17 @@ a.grey-link padding: 0 text-align: right flex: 0 0 calc(100% - 110px) - &:nth-child(2), - &:last-child + &:nth-child(2) flex: 0 0 60px &:nth-child(3) - flex: 0 0 calc(100% - 230px) + flex: 0 0 calc(100% - 290px) +t padding: 0 - flex: 0 0 calc(100% - 60px) + flex: 0 0 calc(100% - 120px) &:last-child padding: 0 text-align: right + flex: 0 0 120px &_blue &__content &:after background: #4A90E2 @@ -3731,6 +3731,8 @@ a.grey-link box-shadow: -40px 0 0 0 $pink-light, 40px 0 0 0 $pink-light +nf border-color: transparent + +m + display: block &__item.open padding-bottom: 40px &__item.open &__toggle @@ -3750,14 +3752,8 @@ a.grey-link &__item.disable &__title, &__item.disable &__content opacity: .4 - &__item.disable &__cell - &:nth-child(1) - justify-content: center - &:nth-child(3) - +m - padding: 0 0 50px &__cell - &:nth-child(1) + &--info display: flex padding-right: 20px flex-direction: column @@ -3766,15 +3762,15 @@ a.grey-link +m padding: 0 flex: 0 0 0 - &:nth-child(2) + margin-bottom: 15px + &--preview padding-right: 20px flex: 0 0 90px - &:nth-child(3) + &--content flex: 0 0 calc(100% - 254px) +m - padding-top: 20px flex: 0 0 calc(100% - 114px) - &:nth-child(4) + &--toggle flex: 0 0 34px &__unlock +m @@ -3785,11 +3781,6 @@ a.grey-link margin-bottom: auto +m display: flex - position: absolute - top: 20px - left: 90px - right: 0 - padding: 0 flex-direction: row &__day position: relative @@ -3816,10 +3807,8 @@ a.grey-link margin: -3px 0 0 auto &__buy +m - position: absolute - left: 0 - bottom: 40px - align-items: center + display: flex + margin-left: 20px &__time margin: 15px 0 opacity: .5 @@ -3852,7 +3841,6 @@ a.grey-link .icon font-size: 8px fill: $pink - &__buy, &__more display: none &__more