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.validators import UniqueValidator
from django.contrib.auth import get_user_model
from apps.course.models import (
Category, Course,
Comment, CourseComment, LessonComment,
@ -21,6 +23,9 @@ from .user import UserSerializer
from .mixins import DispatchContentMixin, DispatchGalleryMixin, DispatchMaterialMixin
User = get_user_model()
class MaterialCreateSerializer(serializers.ModelSerializer):
class Meta:
@ -71,16 +76,22 @@ class LikeCreateSerializer(serializers.ModelSerializer):
class Meta:
model = Like
fields = ['obj_type', 'obj_id']
fields = ['user', 'obj_type', 'obj_id']
def create(self, validated_data):
user = self.context['request'].user
if not (user and user.is_authenticated):
# FIXME
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()
obj_type = validated_data.pop('obj_type')
obj_id = validated_data.pop('obj_id')
if obj_type == self.OBJ_TYPE_CONTEST_WORK:
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():
return Like()
like = Like.objects.create(user=user)

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

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

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

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

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

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

@ -1,7 +1,7 @@
{% load static %}
<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">
<a class="share__item facebook" href="#" data-url="http://{{request.META.HTTP_HOST}}{{object.get_absolute_url}}">
<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('contest/create', ContestEditView.as_view(), name='contest_create'),
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'),
]

@ -1,6 +1,10 @@
<template>
<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>
</template>
@ -17,6 +21,7 @@
page: 1,
lastPage: null,
loading: false,
loaded: false,
contestWorks: [],
};
},
@ -39,8 +44,9 @@
api.get(`/api/v1/contest-works/?contest=${this.contestId}&page=${this.page}`)
.then((response) => {
this.loading = false;
this.loaded = true;
if(this.page > 1){
this.contestWorks.concat(response.data.results);
this.contestWorks = this.contestWorks.concat(response.data.results);
}
else{
this.contestWorks = response.data.results;
@ -48,6 +54,7 @@
})
.catch((response) => {
this.loading = false;
this.loaded = true;
if(response.response.status == 404){
this.lastPage = this.page;
}
@ -60,8 +67,20 @@
<style>
.contest-works {
width: 100%;
}
.contest-works__works {
column-width: 300px;
column-gap: 20px;
text-align: left;
}
.contest-works__loader {
width: 100%;
height: 30px;
position: relative;
}
.contest-works__no-works {
text-align: center;
width: 100%;
}
</style>

@ -34,7 +34,7 @@
</div>
<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>
</form>
</div>
@ -85,7 +85,9 @@
};
},
mounted() {
$('[data-show-upload-contest-work]').click(() => {
$('[data-show-upload-contest-work]').click((e) => {
e.preventDefault();
e.stopPropagation();
this.clear();
this.show();
});
@ -127,7 +129,7 @@
},
save() {
if(! this.validate()) {
return;
return false;
}
let data = this.contestWork;
data.contest = this.contestId;
@ -138,7 +140,7 @@
}
});
request.then((response) => {
if(response.data.id){
if(+response.data.id){
this.$emit('add:contest-work', response.data);
this.hide();
window.location.reload();

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

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

@ -4139,3 +4139,15 @@ a
&__bio
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