remotes/origin/hotfix/LIL-661
gzbender 8 years ago
parent 291fe68821
commit 18a138db6b
  1. 17
      api/v1/serializers/course.py
  2. 11
      api/v1/views.py
  3. 9
      apps/content/templates/content/contest.html
  4. 6
      apps/content/templates/content/contest_work.html
  5. 1
      apps/content/views.py
  6. 3
      project/templates/blocks/header.html
  7. 2
      project/templates/blocks/lil_store_js.html
  8. 2
      project/templates/blocks/share.html
  9. 2
      project/urls.py
  10. 23
      web/src/components/ContestWorks.vue
  11. 10
      web/src/components/UploadContestWork.vue
  12. 41
      web/src/components/blocks/Likes.vue
  13. 5
      web/src/js/modules/popup.js
  14. 12
      web/src/sass/_common.sass

@ -1,6 +1,8 @@
from rest_framework import serializers from rest_framework import serializers
from rest_framework.validators import UniqueValidator from rest_framework.validators import UniqueValidator
from django.contrib.auth import get_user_model
from apps.course.models import ( from apps.course.models import (
Category, Course, Category, Course,
Comment, CourseComment, LessonComment, Comment, CourseComment, LessonComment,
@ -21,6 +23,9 @@ from .user import UserSerializer
from .mixins import DispatchContentMixin, DispatchGalleryMixin, DispatchMaterialMixin from .mixins import DispatchContentMixin, DispatchGalleryMixin, DispatchMaterialMixin
User = get_user_model()
class MaterialCreateSerializer(serializers.ModelSerializer): class MaterialCreateSerializer(serializers.ModelSerializer):
class Meta: class Meta:
@ -71,16 +76,22 @@ class LikeCreateSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Like model = Like
fields = ['obj_type', 'obj_id'] fields = ['user', 'obj_type', 'obj_id']
def create(self, validated_data): def create(self, validated_data):
user = self.context['request'].user # FIXME
if not (user and user.is_authenticated): if validated_data.get('user'):
user = validated_data.get('user')
else:
user = self.context['request'].user
if not user: # FIXME and user.is_authenticated):
return Like() return Like()
obj_type = validated_data.pop('obj_type') obj_type = validated_data.pop('obj_type')
obj_id = validated_data.pop('obj_id') obj_id = validated_data.pop('obj_id')
if obj_type == self.OBJ_TYPE_CONTEST_WORK: if obj_type == self.OBJ_TYPE_CONTEST_WORK:
contest_work = ContestWork.objects.get(pk=obj_id) contest_work = ContestWork.objects.get(pk=obj_id)
# FIXME in prod:
# if contest_work.user == user or contest_work.likes.filter(user=user).exists():
if contest_work.likes.filter(user=user).exists(): if contest_work.likes.filter(user=user).exists():
return Like() return Like()
like = Like.objects.create(user=user) like = Like.objects.create(user=user)

@ -113,6 +113,8 @@ class BanerViewSet(ExtendedModelViewSet):
class ImageObjectViewSet(ExtendedModelViewSet): class ImageObjectViewSet(ExtendedModelViewSet):
queryset = ImageObject.objects.all() queryset = ImageObject.objects.all()
serializer_class = ImageObjectSerializer serializer_class = ImageObjectSerializer
# FIXME
authentication_classes = []
# permission_classes = (IsAuthorOrAdmin,) # permission_classes = (IsAuthorOrAdmin,)
@ -138,6 +140,8 @@ class LikeViewSet(ExtendedModelViewSet):
search_fields = ('user__email', 'user__firstname', 'user__lastname',) search_fields = ('user__email', 'user__firstname', 'user__lastname',)
ordering_fields = ('created_at', 'update_at',) ordering_fields = ('created_at', 'update_at',)
# permission_classes = (IsAdmin,) # permission_classes = (IsAdmin,)
# FIXME
authentication_classes = []
class CategoryViewSet(ExtendedModelViewSet): class CategoryViewSet(ExtendedModelViewSet):
@ -469,8 +473,11 @@ class ContestWorkViewSet(ExtendedModelViewSet):
'retrieve': ContestWorkSerializer, 'retrieve': ContestWorkSerializer,
} }
filter_fields = ('contest',) filter_fields = ('contest',)
# FIXME
authentication_classes = []
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
if ContestWork.objects.filter(user=request.user).exists(): # FIXME in prod:
return Response(status=status.HTTP_400_BAD_REQUEST) # if ContestWork.objects.filter(user=request.user).exists():
# return Response(status=status.HTTP_400_BAD_REQUEST)
return super().create(request, *args, **kwargs) return super().create(request, *args, **kwargs)

@ -12,9 +12,10 @@
{{ contest.description }} {{ contest.description }}
</div> </div>
<div class="main__actions"> <div class="main__actions">
{% if not contest_work_uploaded %}
<a class="main__btn btn" href="#" data-show-upload-contest-work>Загрузить свою работу</a> <a class="main__btn btn" href=""
{% endif %} {# FIXME in prod: if request.user.is_authenticated and not contest_work_uploaded #}
{% if request.user.is_authenticated %}data-show-upload-contest-work{% else %}data-popup=".js-popup-auth"{% endif %}>Загрузить свою работу</a>
</div> </div>
</div> </div>
</div> </div>
@ -29,7 +30,7 @@
<div class="section"> <div class="section">
<div class="section__center center"> <div class="section__center center">
<a id="gallery" name="gallery"> <a id="gallery" name="gallery">
<div class="title title_center">Галлерея</div> <div class="title title_center">Галерея</div>
</a> </a>
<div class="text"> <div class="text">
</div> </div>

@ -8,13 +8,13 @@
<div class="section" style="padding-bottom: 25px;"> <div class="section" style="padding-bottom: 25px;">
<div class="section__center center center_sm"> <div class="section__center center center_sm">
<div class="go"> <div class="go">
<a class="go__item" href="{% url 'contest' contest_work.contest.id %}"> <a class="go__item" href="{% url 'contest' contest_work.contest.slug %}">
<div class="go__arrow"> <div class="go__arrow">
<svg class="icon icon-arrow-left"> <svg class="icon icon-arrow-left">
<use xlink:href="{% static 'img/sprite.svg' %}#icon-arrow-left"></use> <use xlink:href="{% static 'img/sprite.svg' %}#icon-arrow-left"></use>
</svg> </svg>
</div> </div>
<div class="go__title">Вернуться к&nbsp;галлерее</div> <div class="go__title">Вернуться к&nbsp;галерее</div>
</a> </a>
</div> </div>
</div> </div>
@ -68,7 +68,7 @@
<div class="section"> <div class="section">
<div class="section__center center center_sm"> <div class="section__center center center_sm">
{% include 'templates/blocks/share.html' %} {% include 'templates/blocks/share.html' with share_object_name='работой' %}
</div> </div>
</div> </div>
{% endblock content %} {% endblock content %}

@ -28,6 +28,7 @@ class ContestView(DetailView):
model = Contest model = Contest
context_object_name = 'contest' context_object_name = 'contest'
template_name = 'content/contest.html' template_name = 'content/contest.html'
query_pk_and_slug = True
def get_context_data(self, *args, **kwargs): def get_context_data(self, *args, **kwargs):
context = super().get_context_data() context = super().get_context_data()

@ -50,6 +50,9 @@
<div class="header__group"> <div class="header__group">
<a class="header__section" href="https://blog.lil.school">БЛОГ</a> <a class="header__section" href="https://blog.lil.school">БЛОГ</a>
</div> </div>
<div class="header__group">
<a class="header__section" href="{% url 'contest' 'august' %}">КОНКУРС</a>
</div>
</nav> </nav>
</div> </div>
{% include 'templates/blocks/user_menu.html' %} {% include 'templates/blocks/user_menu.html' %}

@ -4,7 +4,7 @@
staticUrl: '{% static "" %}', staticUrl: '{% static "" %}',
accessToken: '{{ request.user.auth_token }}', accessToken: '{{ request.user.auth_token }}',
user: { user: {
id: '{{ request.user.id }}', id: {{ request.user.id|default:'null' }},
} }
}; };
</script> </script>

@ -1,7 +1,7 @@
{% load static %} {% load static %}
<div class="share"> <div class="share">
<div class="share__title">Поделиться {% if livelesson or lesson %}уроком{% else %}курсом{% endif %}</div> <div class="share__title">Поделиться {% if share_object_name %}{{ share_object_name }}{% else %}{% if livelesson or lesson %}уроком{% else %}курсом{% endif %}{% endif %}</div>
<div class="share__list likely"> <div class="share__list likely">
<a class="share__item facebook" href="#" data-url="http://{{request.META.HTTP_HOST}}{{object.get_absolute_url}}"> <a class="share__item facebook" href="#" data-url="http://{{request.META.HTTP_HOST}}{{object.get_absolute_url}}">
<svg class="icon icon-share-facebook"> <svg class="icon icon-share-facebook">

@ -85,7 +85,7 @@ urlpatterns = [
path('test', TemplateView.as_view(template_name='templates/lilcity/test.html'), name='test'), path('test', TemplateView.as_view(template_name='templates/lilcity/test.html'), name='test'),
path('contest/create', ContestEditView.as_view(), name='contest_create'), path('contest/create', ContestEditView.as_view(), name='contest_create'),
path('contest/<int:pk>/edit', ContestEditView.as_view(), name='contest_edit'), path('contest/<int:pk>/edit', ContestEditView.as_view(), name='contest_edit'),
path('contest/<int:pk>/', ContestView.as_view(), name='contest'), path('contest/<str:slug>/', ContestView.as_view(), name='contest'),
path('contest-work/<int:pk>/', ContestWorkView.as_view(), name='contest_work'), path('contest-work/<int:pk>/', ContestWorkView.as_view(), name='contest_work'),
] ]

@ -1,6 +1,10 @@
<template> <template>
<div class="contest-works"> <div class="contest-works">
<contest-work v-for="contestWork in contestWorks" :contest-work="contestWork"></contest-work> <div class="contest-works__works">
<contest-work v-for="contestWork in contestWorks" :key="contestWork.id" :contest-work="contestWork"></contest-work>
</div>
<div v-show="loading" class="contest-works__loader"><div class="loading-loader"></div></div>
<div v-if="loaded && !contestWorks.length" class="contest-works__no-works">Здесь вы сможете увидеть работы участников после их добавления</div>
</div> </div>
</template> </template>
@ -17,6 +21,7 @@
page: 1, page: 1,
lastPage: null, lastPage: null,
loading: false, loading: false,
loaded: false,
contestWorks: [], contestWorks: [],
}; };
}, },
@ -39,8 +44,9 @@
api.get(`/api/v1/contest-works/?contest=${this.contestId}&page=${this.page}`) api.get(`/api/v1/contest-works/?contest=${this.contestId}&page=${this.page}`)
.then((response) => { .then((response) => {
this.loading = false; this.loading = false;
this.loaded = true;
if(this.page > 1){ if(this.page > 1){
this.contestWorks.concat(response.data.results); this.contestWorks = this.contestWorks.concat(response.data.results);
} }
else{ else{
this.contestWorks = response.data.results; this.contestWorks = response.data.results;
@ -48,6 +54,7 @@
}) })
.catch((response) => { .catch((response) => {
this.loading = false; this.loading = false;
this.loaded = true;
if(response.response.status == 404){ if(response.response.status == 404){
this.lastPage = this.page; this.lastPage = this.page;
} }
@ -60,8 +67,20 @@
<style> <style>
.contest-works { .contest-works {
width: 100%;
}
.contest-works__works {
column-width: 300px; column-width: 300px;
column-gap: 20px; column-gap: 20px;
text-align: left; text-align: left;
} }
.contest-works__loader {
width: 100%;
height: 30px;
position: relative;
}
.contest-works__no-works {
text-align: center;
width: 100%;
}
</style> </style>

@ -34,7 +34,7 @@
</div> </div>
<div class="field" style="text-align: center;"> <div class="field" style="text-align: center;">
<button class="btn" tabindex="3" @click="save">Отправить на конкурс</button> <button class="btn" tabindex="3" @click.prevent="save">Отправить на конкурс</button>
</div> </div>
</form> </form>
</div> </div>
@ -85,7 +85,9 @@
}; };
}, },
mounted() { mounted() {
$('[data-show-upload-contest-work]').click(() => { $('[data-show-upload-contest-work]').click((e) => {
e.preventDefault();
e.stopPropagation();
this.clear(); this.clear();
this.show(); this.show();
}); });
@ -127,7 +129,7 @@
}, },
save() { save() {
if(! this.validate()) { if(! this.validate()) {
return; return false;
} }
let data = this.contestWork; let data = this.contestWork;
data.contest = this.contestId; data.contest = this.contestId;
@ -138,7 +140,7 @@
} }
}); });
request.then((response) => { request.then((response) => {
if(response.data.id){ if(+response.data.id){
this.$emit('add:contest-work', response.data); this.$emit('add:contest-work', response.data);
this.hide(); this.hide();
window.location.reload(); window.location.reload();

@ -1,6 +1,7 @@
<template> <template>
<div class="likes" :class="{ 'likes_liked': userLikedProp }"> <div class="likes" :class="{ 'likes_liked': userLikedProp }">
<span>{{ likesProp }}</span><span class="likes__like" @click.prevent="addLike"> <span>{{ likesProp }}</span><span class="likes__like" @click="addLike"
v-bind="{ 'data-popup': $root.store.user.id ? '' : '.js-popup-auth' }">
<svg class="likes__icon icon icon-like"> <svg class="likes__icon icon icon-like">
<use v-bind="{'xlink:href': $root.store.staticUrl + 'img/sprite.svg' + '#icon-like' }"></use> <use v-bind="{'xlink:href': $root.store.staticUrl + 'img/sprite.svg' + '#icon-like' }"></use>
</svg></span> </svg></span>
@ -20,25 +21,29 @@
} }
}, },
methods: { methods: {
addLike() { addLike(event) {
if(this._userLiked){ if(this.userLikedProp){
return; return;
} }
api.post('/api/v1/likes/', { if(this.$root.store.user.id) {
obj_type: this.objType, event.stopPropagation();
obj_id: this.objId, api.post('/api/v1/likes/', {
}, { user: this.$root.store.user.id, // FIXME
headers: { obj_type: this.objType,
'Authorization': `Token ${this.$root.store.accessToken}`, obj_id: this.objId,
} }, {
}) headers: {
.then((response) => { 'Authorization': `Token ${this.$root.store.accessToken}`,
if(response.data && response.data.id) { }
this.userLikedProp = true; })
this.likesProp += 1; .then((response) => {
this.$emit('liked'); if (response.data && response.data.id) {
} this.userLikedProp = true;
}) this.likesProp += 1;
this.$emit('liked');
}
});
}
} }
} }
} }

@ -6,10 +6,13 @@ $(document).ready(function () {
popup; popup;
body.on('click', '[data-popup]', function(e){ body.on('click', '[data-popup]', function(e){
let data = $(this).data('popup');
if(! data) {
return true;
}
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
let data = $(this).data('popup');
popup = $(data); popup = $(data);
showPopup(); showPopup();

@ -4139,3 +4139,15 @@ a
&__bio &__bio
flex: calc(100% - 70px); flex: calc(100% - 70px);
.loading-loader
content: ''
position: absolute
top: 50%
left: 50%
width: 24px
height: 24px
margin: -12px 0 0 -12px
border: 3px solid #B5B5B5
border-left: 3px solid transparent
border-radius: 50%
animation: loading .6s infinite linear

Loading…
Cancel
Save