Merge branch 'master' of gitlab.com:lilcity/backend into hotfix/LIL-724

remotes/origin/hotfix/LIL-724
gzbender 7 years ago
commit 236fd212fc
  1. 7
      api/v1/views.py
  2. 7
      apps/content/admin.py
  3. 5
      apps/content/templates/content/contest.html
  4. 9
      apps/content/templates/content/contest_edit.html
  5. 0
      apps/course/management/__init__.py
  6. 0
      apps/course/management/commands/__init__.py
  7. 20
      apps/course/management/commands/update_courses_slug.py
  8. 25
      apps/course/models.py
  9. 3
      apps/course/templates/course/_items.html
  10. 3
      apps/course/templates/course/course.html
  11. 17
      apps/course/templates/course/course_edit.html
  12. 2
      apps/course/templates/course/course_only_lessons.html
  13. 2
      apps/course/templates/course/lesson.html
  14. 26
      apps/course/views.py
  15. 2
      apps/school/models.py
  16. 2
      apps/school/templates/school/livelessons_list.html
  17. 6
      apps/school/urls.py
  18. 7
      apps/user/admin.py
  19. 22
      apps/user/migrations/0029_emaillog.py
  20. 11
      apps/user/models.py
  21. 4
      apps/user/templates/user/bonus-history.html
  22. 4
      apps/user/templates/user/profile-settings.html
  23. 12
      project/templates/blocks/lil_store_js.html
  24. 8
      project/templates/lilcity/edit_index.html
  25. 1
      requirements.txt
  26. 1
      web/package.json
  27. 3
      web/src/components/Comments.vue
  28. 4
      web/src/components/ContestRedactor.vue
  29. 29
      web/src/components/ContestWorks.vue
  30. 212
      web/src/components/CourseRedactor.vue
  31. 38
      web/src/components/UploadContestWork.vue
  32. 35
      web/src/components/blocks/ContestWork.vue
  33. 20
      web/src/js/app.js
  34. 4
      web/src/js/contest-redactor.js
  35. 9
      web/src/js/modules/api.js
  36. 5
      web/src/js/pages/contest-edit.js
  37. 7
      web/src/js/pages/contest.js
  38. 6
      web/src/js/pages/course-edit.js
  39. 6
      web/src/js/pages/profile.js
  40. 5
      web/src/js/pages/user-gallery-edit.js
  41. 2
      web/src/sass/app.sass
  42. 35
      web/src/sass/components/contest-edit.scss
  43. 55
      web/src/sass/components/contest.scss
  44. 166
      web/src/sass/components/course-edit.scss
  45. 166
      web/src/sass/pages/course-edit.scss
  46. 13
      web/webpack.config.js

@ -71,7 +71,7 @@ from apps.payment.models import (
CoursePayment, SchoolPayment, UserBonus,
)
from apps.school.models import SchoolSchedule, LiveLesson
from apps.user.models import AuthorRequest
from apps.user.models import AuthorRequest, EmailLog
from project.pusher import pusher
from project.sengrid import get_sendgrid_client
@ -694,6 +694,11 @@ class CaptureEmail(views.APIView):
email = request.data.get('email')
sg = get_sendgrid_client()
if not email:
return Response({'error': 'No email'}, status=status.HTTP_400_BAD_REQUEST)
EmailLog.objects.create(email=email, source=EmailLog.SOURCE_TRIAL_LESSON)
# берем все списки
response = sg.client.contactdb.lists.get()
if response.status_code != 200:

@ -8,7 +8,7 @@ from polymorphic.admin import (
from apps.content.models import (
Baner, Content, Image, Text, ImageText, Video,
Gallery, GalleryImage, ImageObject,
Contest,ContestWork,
Contest, ContestWork, FAQ,
)
@ -90,3 +90,8 @@ class ContestAdmin(admin.ModelAdmin):
@admin.register(ContestWork)
class ContestWorkAdmin(admin.ModelAdmin):
base_model = ContestWork
@admin.register(FAQ)
class FAQAdmin(admin.ModelAdmin):
base_model = FAQ

@ -45,3 +45,8 @@
</div>
</div>
{% endblock content %}
{% block pre_app_js %}
<script type="text/javascript" src="{% static 'contest.js' %}"></script>
<link rel="stylesheet" href="{% static 'contest.css' %}" />
{% endblock pre_app_js %}

@ -12,7 +12,8 @@
{% block content %}
<contest-redactor {% if object and object.id %}:contest-id="{{ object.id }}"{% endif %}></contest-redactor>
{% endblock content %}
{% block foot %}
<script type="text/javascript" src="{% static 'contestRedactor.js' %}"></script>
<link rel="stylesheet" href="{% static 'contestRedactor.css' %}" />
{% endblock foot %}
{% block pre_app_js %}
<script type="text/javascript" src="{% static 'contestEdit.js' %}"></script>
<link rel="stylesheet" href="{% static 'contestEdit.css' %}" />
{% endblock pre_app_js %}

@ -0,0 +1,20 @@
from unidecode import unidecode
from django.core.management.base import BaseCommand, CommandError
from django.db.models import Q
from django.utils.text import slugify
from apps.course.models import Course
class Command(BaseCommand):
help = 'Upload users to Roistat'
def handle(self, *args, **options):
courses = Course.objects.filter(Q(slug__isnull=True) | Q(slug=''))
for course in courses:
course.slug = slugify(unidecode(course.title[:90]))
try:
course.save()
except:
course.slug += str(course.id)
course.save()

@ -1,10 +1,13 @@
import arrow
from random import shuffle
from uuid import uuid4
from unidecode import unidecode
from django.db import models
from django.core.exceptions import ValidationError
from django.utils import timezone
from django.utils.timezone import now
from django.utils.text import slugify
from django.contrib.auth import get_user_model
from django.urls import reverse_lazy
from django.conf import settings
@ -119,24 +122,24 @@ class Course(BaseModel, DeactivatedMixin):
def __str__(self):
return str(self.id) + ' ' + self.title
# def save(self, *args, **kwargs):
# if not self.slug:
# self.slug = slugify(
# self.title[:90],
# allow_unicode=True
# )
# if Course.objects.filter(slug=self.slug).exclude(id=self.id).exists():
# self.slug += '_' + str(uuid4())[:6]
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(unidecode(self.title[:90]))
if self.slug:
if self.slug.isdigit():
self.slug = 'course%s' % self.slug
slug = self.slug
while Course.objects.filter(slug__iexact=self.slug).exclude(id=self.id).exists():
self.slug = '%s_%s' % (slug, str(uuid4())[-4:])
# return super().save()
return super().save()
@property
def url(self):
return self.get_absolute_url()
def get_absolute_url(self):
return reverse_lazy('course', args=[self.slug or self.id])
return reverse_lazy('course', args=[self.slug.lower() if self.slug else self.id])
@property
def is_free(self):

@ -53,7 +53,8 @@
{% if course.old_price %}
<div class="courses__old-price"><s>{{ course.old_price|floatformat:"-2" }}₽</s></div>
{% endif %}
<div class="courses__price">{{ course.price|floatformat:"-2" }}₽</div>
<div class="courses__price"
{% if course.old_price %}style="color: red;"{% endif %}>{{ course.price|floatformat:"-2" }}₽</div>
{% endif %}
</div>
<a class="courses__title" href="{{ course.url }}">{{ course.title }}</a>

@ -335,8 +335,7 @@
</div>
<div class="section section_gray">
<div class="section__center center center_sm">
<div class="title">Задавайте вопросы:</div>
<div class="title">{% if request.user.is_authenticated %}Задавайте вопросы:{% else %}Комментарии:{% endif %}</div>
<comments obj-type="course" obj-id="{{ course.id }}"></comments>
</div>
</div>

@ -7,13 +7,22 @@
Создание {% if live == 'true' %}стрима{% else %}курса{% endif %}
{% endif %}
{% endblock title%}
{% block header_buttons %}
{% if live == 'true' %}
<button id="course-redactor__publish-button" class="header__btn btn" disabled>Сохранить</button>
{% else %}
<button id="course-redactor__publish-button" class="header__btn btn" disabled>На модерацию</button>
{% endif %}
{% endblock header_buttons %}
{% block content %}
<course-redactor :live="{{ live }}" author-picture="{% if request.user.photo %}{{ request.user.photo.url }}{% else %}{% static 'img/user_default.jpg' %}{% endif %}"
author-name="{{ request.user.first_name }} {{ request.user.last_name }}"
access-token="{{ request.user.auth_token }}"
{% if object and object.id %}:course-id="{{ object.id }}"{% endif %}></course-redactor>
{% endblock content %}
{% block foot %}
<script type="text/javascript" src="{% static 'courseRedactor.js' %}"></script>
<link rel="stylesheet" href="{% static 'courseRedactor.css' %}" />
{% endblock foot %}
{% block pre_app_js %}
<script type="text/javascript" src="{% static 'courseEdit.js' %}"></script>
<link rel="stylesheet" href="{% static 'courseEdit.css' %}" />
{% endblock pre_app_js %}

@ -192,7 +192,7 @@
{% endif %}
<div class="section section_gray">
<div class="section__center center center_sm">
<div class="title">Задавайте вопросы:</div>
<div class="title">{% if request.user.is_authenticated %}Задавайте вопросы:{% else %}Комментарии:{% endif %}</div>
<comments obj-type="course" obj-id="{{ course.id }}"></comments>
</div>
</div>

@ -113,7 +113,7 @@
</div> {% endcomment %}
<div class="section section_gray">
<div class="section__center center center_sm">
<div class="title">Задавайте вопросы:</div>
<div class="title">{% if request.user.is_authenticated %}Задавайте вопросы:{% else %}Комментарии:{% endif %}</div>
<comments obj-type="lesson" obj-id="{{ lesson.id }}"></comments>
</div>
</div>

@ -200,6 +200,32 @@ class CourseView(DetailView):
# ((self.object.status != Course.PUBLISHED and request.user.role != User.ADMIN_ROLE) or
# (self.object.status != Course.PUBLISHED and request.user.role != User.AUTHOR_ROLE and self.object.author != request.user)):
def get_object(self, queryset=None):
if queryset is None:
queryset = self.get_queryset()
pk = self.kwargs.get(self.pk_url_kwarg)
slug = self.kwargs.get(self.slug_url_kwarg)
if pk is not None:
queryset = queryset.filter(pk=pk)
if slug is not None and (pk is None or self.query_pk_and_slug):
slug_field = self.get_slug_field()
queryset = queryset.filter(**{'%s__iexact' % slug_field: slug})
if pk is None and slug is None:
raise AttributeError("Generic detail view %s must be called with "
"either an object pk or a slug."
% self.__class__.__name__)
try:
# Get the single item from the filtered queryset
obj = queryset.get()
except queryset.model.DoesNotExist:
raise Http404(_("No %(verbose_name)s found matching the query") %
{'verbose_name': queryset.model._meta.verbose_name})
return obj
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if self.request.user.is_authenticated:

@ -147,7 +147,7 @@ class LiveLesson(BaseModel, DeactivatedMixin):
return self.get_absolute_url()
def get_absolute_url(self):
return reverse_lazy('school:lesson-detail', args=[self.date.strftime('%d-%m-%y')])
return reverse_lazy('school:lesson-detail', kwargs={'lesson_date': self.date.strftime('%d-%m-%y')})
def stream_index(self):
return self.stream.split('/')[-1]

@ -11,7 +11,7 @@
{% for livelesson in livelesson_list %}
<div class="lessons__item">
<div class="lessons__actions lessons__actions__no-hover">
<a target="_blank" class="lessons__action" href="{{ live_lesson.url }}">
<a target="_blank" class="lessons__action" href="{{ livelesson.url }}">
<svg class="icon icon-eye">
<use xlink:href="{% static 'img/sprite.svg' %}#icon-eye"></use>
</svg>

@ -1,4 +1,4 @@
from django.urls import path, include
from django.urls import path, re_path
from .views import (
LiveLessonsView, LiveLessonEditView,
@ -12,6 +12,6 @@ urlpatterns = [
path('lessons/', LiveLessonsView.as_view(), name='lessons'),
path('lessons/create', LiveLessonEditView.as_view(), name='lessons-create'),
path('lessons/<int:pk>/edit', LiveLessonEditView.as_view(), name='lessons-edit'),
path('lessons/<int:pk>/', LiveLessonsDetailView.as_view(), name='lesson-detail'),
path('<str:lesson_date>', LiveLessonsDetailView.as_view(), name='lesson-detail'),
path('lessons/<int:pk>/', LiveLessonsDetailView.as_view(), name='lesson-detail-id'),
re_path(r'(?P<lesson_date>\d+\-\d+\-\d+)', LiveLessonsDetailView.as_view(), name='lesson-detail'),
]

@ -3,7 +3,7 @@ from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.utils.translation import gettext_lazy as _
from .models import AuthorRequest, EmailSubscription, SubscriptionCategory
from .models import AuthorRequest, EmailSubscription, SubscriptionCategory, EmailLog
User = get_user_model()
@ -47,3 +47,8 @@ class EmailSubscriptionAdmin(admin.ModelAdmin):
'user',
'email',
)
@admin.register(EmailLog)
class EmailLogAdmin(admin.ModelAdmin):
pass

@ -0,0 +1,22 @@
# Generated by Django 2.0.7 on 2019-01-25 08:18
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('user', '0028_auto_20181214_0107'),
]
operations = [
migrations.CreateModel(
name='EmailLog',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('email', models.EmailField(max_length=254, verbose_name='email address')),
('created_at', models.DateTimeField(auto_now_add=True)),
('source', models.PositiveSmallIntegerField(choices=[(1, 'Пробный урок')])),
],
),
]

@ -304,3 +304,14 @@ class Referral(models.Model):
from apps.payment.models import UserBonus
UserBonus.objects.create(user=self.referral, amount=referral_bonus, payment=self.payment, referral=self)
UserBonus.objects.create(user=self.referrer, amount=referrer_bonus, payment=self.payment, referral=self)
class EmailLog(models.Model):
SOURCE_TRIAL_LESSON = 1
SOURCE_CHOICES = (
(SOURCE_TRIAL_LESSON, 'Пробный урок'),
)
email = models.EmailField(_('email address'))
created_at = models.DateTimeField(auto_now_add=True)
source = models.PositiveSmallIntegerField(choices=SOURCE_CHOICES)

@ -79,3 +79,7 @@
</div>
</div>
{% endblock content %}
{% block pre_app_js %}
<script type="text/javascript" src="{% static 'profile.js' %}"></script>
{% endblock pre_app_js %}

@ -323,3 +323,7 @@
// (new Inputmask('+7 (999) 999-99-99')).mask(document.querySelector('[name=phone]'));
</script>
{% endblock foot %}
{% block pre_app_js %}
<script type="text/javascript" src="{% static 'profile.js' %}"></script>
{% endblock pre_app_js %}

@ -21,10 +21,14 @@
components: {},
urls: {
courses: "{% url 'courses' %}",
courseEdit: /\/course\/\d+\/edit/,
courseCreate: "{% url 'course_create' %}",
userProfileEdit: "{% url 'user-edit-profile' %}",
userProfile: "{% url 'user-profile' %}",
userBonuses: "{% url 'user-bonuses' %}",
faq: "{% url 'faq' %}",
contestEdit: /contest\/\w+\/edit/,
userGalleryEdit: "{% url 'user-gallery-edit' %}",
},
flags: {
referrer: '{{ referrer.id|default:'' }}',
@ -33,8 +37,12 @@
isGiftCertificateUrl: {{ is_gift_certificate_url|yesno:"true,false" }},
},
data: {},
urlIs: (urlPatternName) => {
return window.location.pathname.search(window.LIL_STORE.urls[urlPatternName]) > -1;
urlIs: (urlPatternNames) => {
if(! Array.isArray(urlPatternNames)){
urlPatternNames = [urlPatternNames];
}
return urlPatternNames.filter(
urlPatternName => window.location.pathname.search(window.LIL_STORE.urls[urlPatternName]) > -1).length > 0;
},
isIndexPage: window.location.pathname == '/',
};

@ -53,11 +53,8 @@
<use xlink:href="{% static 'img/sprite.svg' %}#icon-eye"></use>
</svg>
</button>
{% if live == 'true' %}
<button id="course-redactor__publish-button" class="header__btn btn" disabled>Сохранить</button>
{% else %}
<button id="course-redactor__publish-button" class="header__btn btn" disabled>На модерацию</button>
{% endif %}
{% block header_buttons %}
{% endblock header_buttons %}
</div>
{% include 'templates/blocks/user_menu.html' %}
</div>
@ -316,6 +313,7 @@
</div>
</div>
{% include 'templates/blocks/lil_store_js.html' %}
{% block pre_app_js %}{% endblock pre_app_js %}
<script>
var schoolDiscount = parseFloat({{ config.SERVICE_DISCOUNT }});
var schoolAmountForDiscount = parseFloat({{ config.SERVICE_DISCOUNT_MIN_AMOUNT }});

@ -36,3 +36,4 @@ short_url
sendgrid
drf_dynamic_fields
flower==0.9.2
unidecode

@ -41,6 +41,7 @@
"baguettebox.js": "^1.10.0",
"clipboard": "^2.0.1",
"downscale": "^1.0.4",
"extract-loader": "^3.1.0",
"glob": "^7.1.2",
"history": "^4.7.2",
"ilyabirman-likely": "^2.3.0",

@ -7,6 +7,7 @@
</li>
</ul>
</div>
<div v-if="!$root.store.user.id && !isChat && noComments">Авторизуйтесь и оставьте первый комментарий!</div>
<comment-form v-if="$root.store.user.id && ! replyTo" :controller="controller"></comment-form>
</div>
</template>
@ -26,6 +27,7 @@
nodes: [],
controller: this,
flatComments: {},
noComments: false,
}
},
methods: {
@ -119,6 +121,7 @@
.then((response) => {
vm.loading = false;
vm.nodes = response.data;
vm.noComments = ! vm.nodes.length;
vm.connectToPusher();
})
.catch(() => {

@ -229,7 +229,3 @@
}
};
</script>
<style>
</style>

@ -74,8 +74,8 @@
}
}
}
heights[column] += workHeight;
heights[column] += workHeight;
[first, second, third][column].push(work);
index++;
}
@ -112,28 +112,3 @@
components: {ContestWork},
}
</script>
<style>
.contest-works {
width: 100%;
}
.contest-works__works {
text-align: left;
display: flex;
}
.contest-works__column {
display: flex;
flex-direction: column;
margin-right: 20px;
width: 300px;
}
.contest-works__loader {
width: 100%;
height: 30px;
position: relative;
}
.contest-works__no-works {
text-align: center;
width: 100%;
}
</style>

@ -34,7 +34,8 @@
v-model="course.title"
placeholder="Добавить заголовок"></textarea>
</div>
<div class="courses__content field" v-bind:class="{ error: ($v.course.short_description.$dirty || showErrors) && $v.course.short_description.$invalid }">
<div class="courses__content field" style="width: 300px;"
v-bind:class="{ error: ($v.course.short_description.$dirty || showErrors) && $v.course.short_description.$invalid }">
<vue-redactor :value.sync="course.short_description" placeholder="Добавить краткое описание"/>
</div>
</div>
@ -45,7 +46,7 @@
<div v-if="!live" class="info__field field">
<div class="field__label field__label_gray">ССЫЛКА</div>
<div class="field__wrap">
<input type="text" class="field__input" v-model="course.url" @input="slugChanged = true">
<input type="text" class="field__input" v-model="course.slug" @input="slugChanged = true">
</div>
<div class="field__wrap field__wrap--additional">{{ courseFullUrl }}</div>
</div>
@ -273,6 +274,7 @@
price: null,
old_price: null,
age: 0,
slug: '',
url: '',
coverImage: '',
kit__body: null,
@ -473,8 +475,8 @@
},
onCourseNameInput() {
this.$v.course.title.$touch();
if (!this.slugChanged) {
this.course.url = slugify(this.course.title);
if (!this.slugChanged && !this.$v.course.status) {
this.course.slug = (slugify(this.course.title) || '').toLowerCase();
}
},
removeLesson(lessonIndex) {
@ -662,7 +664,7 @@
if(this.live) {
window.location = '/school/lessons';
} else {
api.publishCourse(this.course.id, this.accessToken)
api.publishCourse(this.course, this.accessToken)
.then((response) => {
window.location = '/course/on-moderation';
})
@ -741,24 +743,6 @@
}
},
saveCourseDraft: function (newValue, oldValue) {
//console.log('saveCourseDraft');
// if (!oldValue.id) {
// return;
// }
// if(this.live) {
// if(!this.course.date || this.course.short_description == '' || this.course.title == '') {
// //console.log('live valiedation error');
// return;
// }
// } else {
// if(this.course.short_description == '' || this.course.title == '') {
// //console.log('course validation error');
// return;
// }
// }
if (this.savingDebounceTimeout) {
clearTimeout(this.savingDebounceTimeout);
}
@ -771,13 +755,14 @@
this.courseSaving = true;
this.changeSavingStatus();
const courseObject = this.course;
courseObject.url = (courseObject.url) ? slugify(courseObject.url):courseObject.url;
courseObject.slug = courseObject.slug && slugify(courseObject.slug);
api.saveCourse(courseObject, this.accessToken)
.then((response) => {
this.courseSaving = false;
this.changeSavingStatus(true);
this.courseSyncHook = true;
const courseData = api.convertCourseJson(response.data);
this.course.slug = courseData.slug;
if (this.course.coverImage) {
courseData.coverImage = this.course.coverImage;
}
@ -804,19 +789,11 @@
})
}
});
if (courseData.url) {
this.slugChanged = true;
}
if(courseData.id) {
this.course.id = courseData.id;
}
/*if(this.live && courseData.date) {
this.course.date = _.find(this.scheduleOptions, function(item){
return item.value == courseData.date;
});
}*/
this.$nextTick(() => {
this.courseSyncHook = false;
});
@ -1029,11 +1006,8 @@
}
},
courseFullUrl() {
if (!this.course.url) {
return `https://lil.city/course/${this.course.id}`;
}
let suffix = this.course.url ? this.course.url : 'ваша_ссылка';
return `https://lil.city/course/${suffix}`;
let suffix = this.course.slug || this.course.id || 'ваша_ссылка';
return `https://lil.school/course/${suffix}`;
},
},
beforeDestroy() {
@ -1074,168 +1048,4 @@
}
</script>
<style lang="scss">
.vdp-datepicker__calendar {
width: 240px;
margin-top: 10px;
padding: 5px;
background: white;
box-shadow: 0 2px 20px 0 rgba(0, 0, 0, 0.1);
z-index: 99 !important;
header {
display: flex;
margin-bottom: 5px;
-ms-flex-align: center;
align-items: center;
}
.prev, .next {
font-size: 0;
cursor: pointer;
order: 1;
width: auto !important;
padding: 10px;
}
.prev {
order: 1;
}
.next {
order: 3;
}
.prev:before, .next:before {
content: '';
display: block;
width: 10px;
height: 10px;
border: solid #E6E6E6;
border-width: 2px 2px 0 0;
}
.prev:after, .next:after {
content: none !important;
}
.prev:before {
transform: rotate(-135deg);
}
.next:before {
transform: rotate(45deg);
}
}
.kit__preview {
img {
width: 140px;
height: 140px;
}
}
.kit__photo {
width: 140px;
height: 140px;
}
.kit__section-remove {
button.sortable__handle {
margin-right: 10px;
cursor: -webkit-grab;
cursor: grab;
svg.icon-hamburger {
width: 1em;
height: 1em;
}
}
}
.sortable-ghost, .sortable-chosen {
background: white;
border-radius: 10px;
}
.course-redactor__preview-button-bg-save {
background-color: #58fffb;
}
.course-redactor__preview-button {
transition: backgroundColor 0.5s ease-in-out;
}
.field_text {
height: 270px;
overflow: scroll;
}
.courses__item {
flex: 0 0 300px;
}
.courses__item .field {
margin-bottom: 0;
}
.courses__content .redactor-box {
overflow-x: visible;
overflow-y: auto;
max-height: 200px;
background: none;
margin-top: 10px;
}
.courses__content .redactor-layer{
background: none;
}
.courses__theme {
flex: 1;
}
.courses__preview {
.upload {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.upload__title {
color: #888888;
font-size: 16px;
margin-top: 100px;
width: 100%;
text-align: center;
}
.upload__file {
width: 100%;
height: 100%;
}
}
.course-delete-cover {
left: 5px;
position: absolute;
bottom: 3px;
}
.field-category .select__head {
font-size: inherit;
line-height: inherit;
height: auto;
}
.datetime-fields {
display: flex;
}
.field-time {
margin-left: 10px;
flex: 50%;
}
</style>

@ -153,41 +153,3 @@
}
}
</script>
<style lang="scss">
.upload-contest-work {
.popup__wrap {
padding: 35px 35px 0;
}
.title {
text-align: center; font-size: 24px;
.text__curve {
right: 55px;
width: 170px;
bottom: -40px;
}
}
.kit__photo {
height: 400px;
}
.kit__photo.has-image {
border: none;
}
.kit__photo-image {
max-height: 400px;
height: auto;
width: auto;
}
.kit__file {
bottom: 0;
}
}
</style>

@ -25,38 +25,3 @@
components: {Likes},
}
</script>
<style lang="scss">
.contest-work-item {
break-inside: avoid;
border-radius: 8px;
overflow: hidden;
margin-bottom: 20px;
transition: opacity .4s ease-in-out;
text-transform: uppercase;
font-weight: bold;
color: black;
border: 1px solid #ececec;
display: block;
}
.contest-work-item__img {
width: 100%;
height: auto;
}
.contest-work-item__info {
display: flex;
padding: 5px 10px;
}
.contest-work-item__age {
color: #919191;
}
.contest-work-item__bio {
flex: calc(100% - 70px);
}
@media only screen and (min-width: 1023px) {
.contest-works:hover .contest-work-item:not(:hover) {
opacity: 0.4;
}
}
</style>

@ -13,7 +13,6 @@ import "./modules/tabs";
import "./modules/popup";
import "./modules/courses";
import "./modules/comments";
import "./modules/comments";
import "./modules/password-show";
import "./modules/notification";
import "./modules/mixpanel";
@ -25,9 +24,8 @@ import Vue from 'vue';
import Vuelidate from 'vuelidate';
import VueAutosize from '../components/directives/autosize'
import Comments from '../components/Comments';
import UploadContestWork from '../components/UploadContestWork.vue';
import ContestWorks from '../components/ContestWorks.vue';
import Likes from '../components/blocks/Likes.vue';
import FAQ from '../components/FAQ.vue';
Vue.use(Vuelidate);
Vue.use(VueAutosize);
@ -38,23 +36,13 @@ if (process.env.NODE_ENV === 'development') {
}
const components = {
UploadContestWork,
ContestWorks,
Likes,
Comments,
'likes': Likes,
'comments': Comments,
'faq': FAQ,
};
Object.assign(components, window.LIL_STORE.components);
if(window.LIL_STORE.urlIs('faq')){
const FAQ = require('../components/FAQ.vue');
components['faq'] = FAQ.default;
}
if(window.LIL_STORE.urlIs('userProfileEdit') || window.LIL_STORE.urlIs('userBonuses')){
const profile = require("./modules/profile");
profile.main();
}
const app = new Vue({
el: '#lilcity-vue-app',
data() {

@ -1,4 +0,0 @@
import ContestRedactor from '../components/ContestRedactor.vue'
window.LIL_STORE.components['contest-redactor'] = ContestRedactor;

@ -113,7 +113,7 @@ export const api = {
deferred_start_at: deferredStart,
duration: courseObject.duration || 0,
is_featured: courseObject.is_featured,
slug: courseObject.url,
slug: (courseObject.slug || '').toLowerCase(),
date: (courseObject.date) ? courseObject.date.value:null,
stream: courseObject.stream,
cover: courseObject.coverImageId ? courseObject.coverImageId : null,
@ -194,7 +194,7 @@ export const api = {
time: deferredTime,
duration: courseJSON.duration,
is_featured: courseJSON.is_featured,
url: courseJSON.slug,
slug: courseJSON.slug,
stream: courseJSON.stream,
coverImageId: courseJSON.cover && courseJSON.cover.id ? courseJSON.cover.id : null,
coverImage: courseJSON.cover && courseJSON.cover.image ? courseJSON.cover.image : null,
@ -437,8 +437,9 @@ export const api = {
}
});
},
publishCourse: (courseId, accessToken) => {
return api.patch(`/api/v1/courses/${courseId}/`, {status: 1}, {
publishCourse: (course, accessToken) => {
course.status = 1;
return api.patch(`/api/v1/courses/${course.id}/`, course, {
headers: {
'Authorization': `Token ${accessToken}`,
}

@ -0,0 +1,5 @@
import '../../sass/components/contest-edit.scss';
import ContestRedactor from '../../components/ContestRedactor.vue';
window.LIL_STORE.components['contest-redactor'] = ContestRedactor;

@ -0,0 +1,7 @@
import '../../sass/components/contest.scss';
import UploadContestWork from '../../components/UploadContestWork.vue';
import ContestWorks from '../../components/ContestWorks.vue';
window.LIL_STORE.components['upload-contest-work'] = UploadContestWork;
window.LIL_STORE.components['contest-works'] = ContestWorks;

@ -1,5 +1,7 @@
import 'babel-polyfill'
import CourseRedactor from '../components/CourseRedactor.vue'
import '../../sass/components/course-edit.scss';
import 'babel-polyfill';
import CourseRedactor from '../../components/CourseRedactor.vue';
import $ from 'jquery';
window.LIL_STORE.components['course-redactor'] = CourseRedactor;

@ -1,9 +1,9 @@
import $ from 'jquery';
import slugify from 'slugify';
import ClipboardJS from 'clipboard';
import {showNotification} from './notification';
import {showNotification} from '../modules/notification';
export const main = () => {
$(document).ready(function () {
if(window.LIL_STORE.urlIs('userBonuses')){
$('#referrer-url').select().click(function(){
$(this).select();
@ -53,4 +53,4 @@ export const main = () => {
});
changeSlug();
}
}
});

@ -1,6 +1,7 @@
import BlockImages from '../components/blocks/BlockImages.vue';
import BlockImages from '../../components/blocks/BlockImages.vue';
import $ from 'jquery';
import {api} from "./modules/api";
import {api} from "../modules/api";
window.LIL_STORE.components['block-images'] = BlockImages;

@ -2,4 +2,4 @@
@import helpers/all
@import generated/sprite-svg
@import common
@import '~baguettebox.js/dist/baguetteBox.min.css';
@import '~baguettebox.js/dist/baguetteBox.min.css';

@ -0,0 +1,35 @@
.upload-contest-work {
.popup__wrap {
padding: 35px 35px 0;
}
.title {
text-align: center; font-size: 24px;
.text__curve {
right: 55px;
width: 170px;
bottom: -40px;
}
}
.kit__photo {
height: 400px;
}
.kit__photo.has-image {
border: none;
}
.kit__photo-image {
max-height: 400px;
height: auto;
width: auto;
}
.kit__file {
bottom: 0;
}
}

@ -0,0 +1,55 @@
.contest-works {
width: 100%;
}
.contest-works__works {
text-align: left;
display: flex;
}
.contest-works__column {
display: flex;
flex-direction: column;
margin-right: 20px;
width: 300px;
}
.contest-works__loader {
width: 100%;
height: 30px;
position: relative;
}
.contest-works__no-works {
text-align: center;
width: 100%;
}
.contest-work-item {
break-inside: avoid;
border-radius: 8px;
overflow: hidden;
margin-bottom: 20px;
transition: opacity .4s ease-in-out;
text-transform: uppercase;
font-weight: bold;
color: black;
border: 1px solid #ececec;
display: block;
}
.contest-work-item__img {
width: 100%;
height: auto;
}
.contest-work-item__info {
display: flex;
padding: 5px 10px;
}
.contest-work-item__age {
color: #919191;
}
.contest-work-item__bio {
flex: calc(100% - 70px);
}
@media only screen and (min-width: 1023px) {
.contest-works:hover .contest-work-item:not(:hover) {
opacity: 0.4;
}
}

@ -0,0 +1,166 @@
.vdp-datepicker__calendar {
width: 240px;
margin-top: 10px;
padding: 5px;
background: white;
box-shadow: 0 2px 20px 0 rgba(0, 0, 0, 0.1);
z-index: 99 !important;
header {
display: flex;
margin-bottom: 5px;
-ms-flex-align: center;
align-items: center;
}
.prev, .next {
font-size: 0;
cursor: pointer;
order: 1;
width: auto !important;
padding: 10px;
}
.prev {
order: 1;
}
.next {
order: 3;
}
.prev:before, .next:before {
content: '';
display: block;
width: 10px;
height: 10px;
border: solid #E6E6E6;
border-width: 2px 2px 0 0;
}
.prev:after, .next:after {
content: none !important;
}
.prev:before {
transform: rotate(-135deg);
}
.next:before {
transform: rotate(45deg);
}
}
.kit__preview {
img {
width: 140px;
height: 140px;
}
}
.kit__photo {
width: 140px;
height: 140px;
}
.kit__section-remove {
button.sortable__handle {
margin-right: 10px;
cursor: -webkit-grab;
cursor: grab;
svg.icon-hamburger {
width: 1em;
height: 1em;
}
}
}
.sortable-ghost, .sortable-chosen {
background: white;
border-radius: 10px;
}
.course-redactor__preview-button-bg-save {
background-color: #58fffb;
}
.course-redactor__preview-button {
transition: backgroundColor 0.5s ease-in-out;
}
.field_text {
height: 270px;
overflow: scroll;
}
.courses__item {
flex: 0 0 300px;
}
.courses__item .field {
margin-bottom: 0;
}
.courses__content .redactor-box {
overflow-x: visible;
overflow-y: auto;
max-height: 200px;
background: none;
margin-top: 10px;
}
.courses__content .redactor-layer{
background: none;
}
.courses__theme {
flex: 1;
}
.courses__price {
margin-left: 20px;
}
.courses__preview {
.upload {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.upload__title {
color: #888888;
font-size: 16px;
margin-top: 100px;
width: 100%;
text-align: center;
}
.upload__file {
width: 100%;
height: 100%;
}
}
.course-delete-cover {
left: 5px;
position: absolute;
bottom: 3px;
}
.field-category .select__head {
font-size: inherit;
line-height: inherit;
height: auto;
}
.datetime-fields {
display: flex;
}
.field-time {
margin-left: 10px;
flex: 50%;
}

@ -0,0 +1,166 @@
.vdp-datepicker__calendar {
width: 240px;
margin-top: 10px;
padding: 5px;
background: white;
box-shadow: 0 2px 20px 0 rgba(0, 0, 0, 0.1);
z-index: 99 !important;
header {
display: flex;
margin-bottom: 5px;
-ms-flex-align: center;
align-items: center;
}
.prev, .next {
font-size: 0;
cursor: pointer;
order: 1;
width: auto !important;
padding: 10px;
}
.prev {
order: 1;
}
.next {
order: 3;
}
.prev:before, .next:before {
content: '';
display: block;
width: 10px;
height: 10px;
border: solid #E6E6E6;
border-width: 2px 2px 0 0;
}
.prev:after, .next:after {
content: none !important;
}
.prev:before {
transform: rotate(-135deg);
}
.next:before {
transform: rotate(45deg);
}
}
.kit__preview {
img {
width: 140px;
height: 140px;
}
}
.kit__photo {
width: 140px;
height: 140px;
}
.kit__section-remove {
button.sortable__handle {
margin-right: 10px;
cursor: -webkit-grab;
cursor: grab;
svg.icon-hamburger {
width: 1em;
height: 1em;
}
}
}
.sortable-ghost, .sortable-chosen {
background: white;
border-radius: 10px;
}
.course-redactor__preview-button-bg-save {
background-color: #58fffb;
}
.course-redactor__preview-button {
transition: backgroundColor 0.5s ease-in-out;
}
.field_text {
height: 270px;
overflow: scroll;
}
.courses__item {
flex: 0 0 300px;
}
.courses__item .field {
margin-bottom: 0;
}
.courses__content .redactor-box {
overflow-x: visible;
overflow-y: auto;
max-height: 200px;
background: none;
margin-top: 10px;
}
.courses__content .redactor-layer{
background: none;
}
.courses__theme {
flex: 1;
}
.courses__price {
margin-left: 20px;
}
.courses__preview {
.upload {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.upload__title {
color: #888888;
font-size: 16px;
margin-top: 100px;
width: 100%;
text-align: center;
}
.upload__file {
width: 100%;
height: 100%;
}
}
.course-delete-cover {
left: 5px;
position: absolute;
bottom: 3px;
}
.field-category .select__head {
font-size: inherit;
line-height: inherit;
height: auto;
}
.datetime-fields {
display: flex;
}
.field-time {
margin-left: 10px;
flex: 50%;
}

@ -5,26 +5,27 @@ const NODE_ENV = process.env.NODE_ENV || 'development';
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const SpriteLoaderPlugin = require('svg-sprite-loader/plugin');
module.exports = {
entry: {
app: "./src/js/app.js",
courseRedactor: "./src/js/course-redactor.js",
contestRedactor: "./src/js/contest-redactor.js",
userGalleryEdit: "./src/js/user-gallery-edit.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",
userGalleryEdit: "./src/js/pages/user-gallery-edit.js",
mixpanel: "./src/js/third_party/mixpanel-2-latest.js",
sprite: glob('./src/icons/*.svg'),
images: glob('./src/img/*.*'),
imagesCertificates: glob('./src/img/user-certificates/*'),
imagesGiftCertificates: glob('./src/img/gift-certificates/*'),
imagesReviews: glob('./src/img/reviews/*'),
fonts: glob('./src/fonts/*')
fonts: glob('./src/fonts/*'),
},
output: {
path: path.join(__dirname, "build"),
filename: NODE_ENV === 'development' ? '[name].js' : '[name].js',
library: '[name]',
//filename: NODE_ENV === 'development' ? '[name].js' : '[name].[id].[chunkhash].js',
//library: '[name]',
publicPath: '/static/',
},
module: {

Loading…
Cancel
Save