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

remotes/origin/editis_13-01-19
gzbender 7 years ago
commit fb7ffdd873
  1. 8
      api/v1/serializers/content.py
  2. 4
      api/v1/urls.py
  3. 9
      api/v1/views.py
  4. 21
      apps/content/migrations/0023_faq.py
  5. 5
      apps/content/models.py
  6. 18
      apps/content/templates/content/faq.html
  7. 16
      apps/content/views.py
  8. 3
      apps/notification/tasks.py
  9. 2
      project/settings.py
  10. 1
      project/templates/blocks/lil_store_js.html
  11. 3
      project/urls.py
  12. 50
      web/src/components/FAQ.vue
  13. 8
      web/src/components/blocks/BlockImageText.vue
  14. 56
      web/src/js/app.js
  15. 4
      web/src/js/modules/profile.js

@ -4,7 +4,7 @@ from django.conf import settings
from apps.content.models import (
Baner, Content, Image, Text, ImageText, Video,
Gallery, GalleryImage, ImageObject,)
Gallery, GalleryImage, ImageObject, FAQ)
from . import Base64ImageField
@ -256,3 +256,9 @@ class ContentSerializer(serializers.ModelSerializer):
return GallerySerializer(obj, context=self.context).to_representation(obj)
return super(ContentSerializer, self).to_representation(obj)
class FAQSerializer(serializers.ModelSerializer):
class Meta:
model = FAQ
fields = '__all__'

@ -19,7 +19,7 @@ from .views import (
SchoolScheduleViewSet, LiveLessonViewSet,
PaymentViewSet, ObjectCommentsViewSet,
ContestViewSet, ContestWorkViewSet,
AuthorBalanceUsersViewSet, CaptureEmail, UserGalleryViewSet)
AuthorBalanceUsersViewSet, CaptureEmail, FAQViewSet, UserGalleryViewSet)
router = DefaultRouter()
router.register(r'author-requests', AuthorRequestViewSet, base_name='author-requests')
@ -41,7 +41,7 @@ router.register(r'image-texts', ImageTextViewSet, base_name='image-texts')
router.register(r'videos', VideoViewSet, base_name='videos')
router.register(r'galleries', GalleryViewSet, base_name='galleries')
router.register(r'gallery-images', GalleryImageViewSet, base_name='gallery-images')
router.register(r'faq', FAQViewSet, base_name='faq')
router.register(r'school-schedules', SchoolScheduleViewSet, base_name='school-schedules')
router.register(r'users', UserViewSet, base_name='users')

@ -28,7 +28,7 @@ from .serializers.content import (
VideoSerializer, VideoCreateSerializer,
GallerySerializer,
GalleryImageSerializer, GalleryImageCreateSerializer,
ImageObjectSerializer,
ImageObjectSerializer, FAQSerializer,
)
from .serializers.school import (
SchoolScheduleSerializer,
@ -63,7 +63,7 @@ from apps.config.models import Config
from apps.content.models import (
Baner, Image, Text, ImageText, Video,
Gallery, GalleryImage, ImageObject,
Contest, ContestWork)
Contest, ContestWork, FAQ)
from apps.payment.models import (
AuthorBalance, Payment,
CoursePayment, SchoolPayment,
@ -719,3 +719,8 @@ class CaptureEmail(views.APIView):
return Response({'status': 'ok'})
class FAQViewSet(ExtendedModelViewSet):
queryset = FAQ.objects.all()
serializer_class = FAQSerializer

@ -0,0 +1,21 @@
# Generated by Django 2.0.7 on 2018-12-04 11:17
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('content', '0022_auto_20180815_2129'),
]
operations = [
migrations.CreateModel(
name='FAQ',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('question', models.TextField(max_length=1000)),
('answer', models.TextField(max_length=1000)),
],
),
]

@ -210,3 +210,8 @@ class ContestWork(models.Model):
def get_absolute_url(self):
return reverse_lazy('contest_work', args=[self.id])
class FAQ(models.Model):
question = models.TextField(max_length=1000,)
answer = models.TextField(max_length=1000,)

@ -0,0 +1,18 @@
{% extends "templates/lilcity/index.html" %}
{% load static %}
{% load jsonify_queryset %}
{% block content %}
<div class="section">
<div class="section__center center center_sm">
<div class="title">Часто задаваемые вопросы</div>
<faq :faqs="$root.store.data.faqs"></faq>
</div>
</div>
{% endblock content %}
{% block pre_app_js %}
<script>
window.LIL_STORE.data.faqs = {{ faqs|safe }};
</script>
{% endblock pre_app_js %}

@ -7,7 +7,7 @@ from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_http_methods
from django.views.generic import TemplateView, DetailView
from apps.content.models import Contest, ContestWork
from apps.content.models import Contest, ContestWork, FAQ
from apps.course.models import ContestWorkComment
@ -110,3 +110,17 @@ def contest_work_comment(request, contest_work_id):
'success': True,
'comment': html,
})
class FAQView(TemplateView):
template_name = 'content/faq.html'
def get(self, request, *args, **kwargs):
context = self.get_context_data(**kwargs)
context['faqs'] = [{
'question': f.question,
'answer': f.answer,
'opened': 0,
} for f in FAQ.objects.all()]
return self.render_to_response(context)

@ -84,6 +84,8 @@ def sendgrid_update_recipients():
for user in users:
last_course_purchase = course_payments.get(user.id) and course_payments.get(user.id).strftime(date_format)
last_school_purchase = school_payments.get(user.id) and school_payments.get(user.id).strftime(date_format)
courses_purchased = CoursePayment.objects.filter(user=user,
status__in=CoursePayment.PW_PAID_STATUSES).values_list('course_id', flat=True)
data.append({
'first_name': user.first_name,
'last_name': user.last_name,
@ -94,6 +96,7 @@ def sendgrid_update_recipients():
# 'gender': {User.NOT_DEFINED: '', User.MALE: 'Мужчина', User.FEMALE: 'Женщина'}[user.gender] if user.gender else '',
'birthday': user.birthday and user.birthday.strftime(date_format),
'date_joined': user.date_joined.strftime(date_format),
'courses_purchased': ','.join(map(str, courses_purchased)) + ',' if courses_purchased else '',
})
sg = get_sendgrid_client()

@ -252,7 +252,7 @@ CELERY_BEAT_SCHEDULE = {
},
'send_certificates': {
'task': 'apps.notification.tasks.send_certificates',
'schedule': crontab(hour=19),
'schedule': crontab(minute=0, hour=19),
'args': (),
},
'sendgrid_update_recipients': {

@ -24,6 +24,7 @@
userProfileEdit: "{% url 'user-edit-profile' %}",
userProfile: "{% url 'user-profile' %}",
userBonuses: "{% url 'user-bonuses' %}",
faq: "{% url 'faq' %}",
},
flags: {
referrer: '{{ referrer.id|default:'' }}',

@ -18,7 +18,7 @@ from django.contrib import admin
from django.views.generic import TemplateView
from django.urls import path, include
from apps.content.views import ContestEditView, ContestView, ContestWorkView, contest_work_comment
from apps.content.views import ContestEditView, ContestView, ContestWorkView, contest_work_comment, FAQView
from apps.course.views import (
CoursesView, likes, coursecomment,
CourseView, LessonView, SearchView,
@ -97,6 +97,7 @@ urlpatterns = [
path('payments/gift-certificate/<int:payment_id>/success', GiftCertificateBuySuccessView.as_view(),
name='gift-certificate-payment-success'),
path('gift-certificate/<str:slug>/get', GiftCertificateGetView.as_view(), name='gift-certificate-get'),
path('faq', FAQView.as_view(), name='faq'),
]

@ -0,0 +1,50 @@
<template>
<div class="faq">
<div class="faq__item" v-for="faq in faqs" :class="{'faq__item_opened': faq.opened}">
<div class="faq__item-head" @click="open(faq)">
<div class="faq__item-question">{{ faq.question }}</div>
<div class="faq__item-opener">
<svg class="icon" :class="{'icon-arrow-up': faq.opened, 'icon-arrow-down': !faq.opened}">
<use xlink:href="/static/img/sprite.svg#icon-arrow-down"></use>
</svg>
</div>
</div>
<div class="faq__item-answer" v-show="faq.opened" style="display: none;">{{ faq.answer }}</div>
</div>
</div>
</template>
<script>
export default {
name: 'faq',
props: ['faqs'],
methods: {
open(faq){
faq.opened = ! faq.opened;
for(let f of this.faqs){
if(f !== faq){
f.opened = false;
}
}
},
},
}
</script>
<style lang="sass">
.faq
&__item-head
display: flex
background: #f8f8f8
padding: 7px 10px 5px
cursor: pointer
&__item-question
flex: 1
&__item-opener
margin-left: 10px
& .icon
width: 16px
height: 16px
&__item-answer
padding: 10px 10px
</style>

@ -15,11 +15,7 @@
</div>
<div class="kit__field field">
<div class="field__wrap">
<textarea class="field__textarea field__textarea_sm"
placeholder="Описание"
:value="text"
v-autosize="text"
@change="onTextChange"></textarea>
<vue-redactor :value="text" v-on:update:value="onTextChange" placeholder="Описание"/>
</div>
</div>
</div>
@ -29,6 +25,7 @@
<script>
import LilImage from "./Image";
import VueRedactor from '../redactor/VueRedactor';
export default {
name: "block-image-text",
@ -49,6 +46,7 @@
},
components: {
'lil-image': LilImage,
'vue-redactor': VueRedactor,
},
}
</script>

@ -15,7 +15,6 @@ import "./modules/courses";
import "./modules/comments";
import "./modules/comments";
import "./modules/password-show";
import {main as profileMain} from "./modules/profile";
import "./modules/notification";
import "./modules/mixpanel";
@ -34,42 +33,37 @@ Vue.use(Vuelidate);
Vue.use(VueAutosize);
if (process.env.NODE_ENV === 'development') {
// Enable vue-devtools
Vue.config.devtools = true;
// Enable vue-devtools
Vue.config.devtools = true;
}
window.urlIs = (urlPatternName) => {
return window.location.pathname.search(window.LIL_STORE.urls[urlPatternName]) > -1;
};
const components = {
UploadContestWork,
ContestWorks,
Likes,
Comments,
UploadContestWork,
ContestWorks,
Likes,
Comments,
};
Object.assign(components, window.LIL_STORE.components);
if(urlIs('faq')){
const FAQ = require('../components/FAQ.vue');
components['faq'] = FAQ.default;
}
if(urlIs('userProfileEdit') || urlIs('userBonuses')){
const profile = require("./modules/profile");
profile.main();
}
const app = new Vue({
el: '#lilcity-vue-app',
data() {
return {
store: window.LIL_STORE,
}
},
mounted(){
if(this.urlIs('userProfileEdit') || this.urlIs('userBonuses')){
profileMain(this);
}
if(this.urlIs('userProfile')){
if(window.location.hash){
$(document).ready(() => {
$(`[href="${window.location.hash}"]`).click();
});
}
}
},
methods: {
urlIs(urlPatternName){
return window.location.pathname.search(this.store.urls[urlPatternName]) > -1;
},
},
components: components
el: '#lilcity-vue-app',
data() {
return {
store: window.LIL_STORE,
} },
components: components
});

@ -3,8 +3,8 @@ import slugify from 'slugify';
import ClipboardJS from 'clipboard';
import {showNotification} from './notification';
export const main = (app) => {
if(app.urlIs('userBonuses')){
export const main = () => {
if(urlIs('userBonuses')){
$('#referrer-url').select().click(function(){
$(this).select();
});

Loading…
Cancel
Save