Merge branch 'master' of gitlab.com:lilcity/backend into feature/LIL-711

remotes/origin/feature/LIL-711
gzbender 7 years ago
commit 13a3d72fe6
  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. 2
      project/templates/blocks/lil_store_js.html
  9. 2
      project/templates/lilcity/index.html
  10. 3
      project/urls.py
  11. 35
      web/src/components/FAQ.vue
  12. 48
      web/src/js/app.js
  13. 4
      web/src/js/modules/profile.js
  14. 20
      web/src/sass/_common.sass

@ -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)
AuthorBalanceUsersViewSet, CaptureEmail, FAQViewSet)
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,
@ -712,3 +712,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)

@ -23,6 +23,7 @@
courses: "{% url 'courses' %}",
userProfileEdit: "{% url 'user-edit-profile' %}",
userBonuses: "{% url 'user-bonuses' %}",
faq: "{% url 'faq' %}",
},
flags: {
referrer: '{{ referrer.id|default:'' }}',
@ -30,5 +31,6 @@
isReferralUrl: {{ is_referral_url|yesno:"true,false" }},
isGiftCertificateUrl: {{ is_gift_certificate_url|yesno:"true,false" }},
},
data: {},
};
</script>

@ -154,7 +154,7 @@
{% include "templates/blocks/popup_capture_email.html" %}
</div>
{% include 'templates/blocks/lil_store_js.html' %}
{% block pre_app_js %}{% endblock pre_app_js %}
<script type="text/javascript" src={% static "app.js" %}></script>
<script>
var schoolDiscount = parseFloat({{ config.SERVICE_DISCOUNT }});

@ -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,
@ -96,6 +96,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,35 @@
<template>
<div class="faq">
<div class="faq__item" v-for="faq in faqs" :class="{'faq__item_opened': faq.opened}">
<div class="faq__item-head">
<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}"
@click="open(faq)">
<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></style>

@ -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";
@ -33,35 +32,38 @@ 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);
}
},
methods: {
urlIs(urlPatternName){
return window.location.pathname.search(this.store.urls[urlPatternName]) > -1;
el: '#lilcity-vue-app',
data() {
return {
store: window.LIL_STORE,
}
},
},
components: components
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();
});

@ -2325,6 +2325,9 @@ a.grey-link
width: 1.47em
height: 1em
.icon-arrow-up
transform: rotate(180deg)
.auth
padding: 0 20px 25px
&__nav
@ -4499,3 +4502,20 @@ a
background: $cyan
&__preview.theme_violet2
background: $viol2
.faq
&__item-head
display: flex
background: #f8f8f8
padding: 7px 10px 5px
&__item-question
flex: 1
&__item-opener
margin-left: 10px
cursor: pointer
& .icon
width: 16px
height: 16px
&__item-answer
padding: 10px 10px

Loading…
Cancel
Save