remotes/origin/feature/drawing-camp
commit
d770acb648
47 changed files with 715 additions and 258 deletions
@ -0,0 +1,28 @@ |
||||
# Generated by Django 2.0.7 on 2019-06-05 13:38 |
||||
|
||||
from django.db import migrations, models |
||||
|
||||
|
||||
class Migration(migrations.Migration): |
||||
|
||||
dependencies = [ |
||||
('config', '0013_config_main_page_video_preview_img'), |
||||
] |
||||
|
||||
operations = [ |
||||
migrations.AddField( |
||||
model_name='config', |
||||
name='NUMBER_OF_CITIES', |
||||
field=models.IntegerField(default=107), |
||||
), |
||||
migrations.AddField( |
||||
model_name='config', |
||||
name='NUMBER_OF_COUNTRIES', |
||||
field=models.IntegerField(default=81), |
||||
), |
||||
migrations.AddField( |
||||
model_name='config', |
||||
name='NUMBER_OF_STUDENTS', |
||||
field=models.IntegerField(default=17000), |
||||
), |
||||
] |
||||
@ -0,0 +1,22 @@ |
||||
{% extends "notification/email/_base.html" %} |
||||
{% load settings %} |
||||
|
||||
{% block content %} |
||||
{% if product_type == 'course' %} |
||||
<p>Курс ждет вас по ссылке <a href="https://{% setting 'MAIN_HOST' %}{{ url }}"> |
||||
https://{% setting 'MAIN_HOST' %}{{ url }}</a></p> |
||||
{% endif %} |
||||
{% if product_type == 'school' %} |
||||
<p>Школа ждет вас по ссылке <a href="https://{% setting 'MAIN_HOST' %}{% url 'school:school' %}"> |
||||
https://{% setting 'MAIN_HOST' %}{% url 'school:school' %}</a></p> |
||||
{% endif %} |
||||
{% if product_type == 'drawing_camp' %} |
||||
<p>Рисовальный лагерь ждет вас по ссылке <a href="https://{% setting 'MAIN_HOST' %}{% url 'school:drawing-camp' %}"> |
||||
https://{% setting 'MAIN_HOST' %}{% url 'school:drawing-camp' %}</a></p> |
||||
{% endif %} |
||||
<p>Так же вы можете найти ссылку в личном кабинете в разделе «Мои покупки».</p> |
||||
|
||||
<p>Занимайтесь с удовольствием!</p> |
||||
|
||||
<p>Команда «Lil School».</p> |
||||
{% endblock content %} |
||||
@ -0,0 +1,26 @@ |
||||
# Generated by Django 2.0.7 on 2019-06-06 21:05 |
||||
|
||||
from django.conf import settings |
||||
from django.db import migrations, models |
||||
import django.db.models.deletion |
||||
|
||||
|
||||
class Migration(migrations.Migration): |
||||
|
||||
dependencies = [ |
||||
('user', '0031_user_review_url'), |
||||
] |
||||
|
||||
operations = [ |
||||
migrations.CreateModel( |
||||
name='Child', |
||||
fields=[ |
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
||||
('first_name', models.CharField(max_length=30, verbose_name='Имя ребенка')), |
||||
('last_name', models.CharField(blank=True, default='', max_length=150, verbose_name='Фамилия ребенка')), |
||||
('gender', models.CharField(choices=[('n', 'не указан'), ('m', 'Мужчина'), ('f', 'Женщина')], default='n', max_length=1, verbose_name='Пол ребенка')), |
||||
('birthday', models.DateField(blank=True, null=True, verbose_name='День рождения ребенка')), |
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='childs', to=settings.AUTH_USER_MODEL)), |
||||
], |
||||
), |
||||
] |
||||
@ -0,0 +1,21 @@ |
||||
# Generated by Django 2.0.7 on 2019-06-07 20:48 |
||||
|
||||
from django.db import migrations |
||||
|
||||
|
||||
def add_childs(apps, schema_editor): |
||||
User = apps.get_model('user', 'User') |
||||
Child = apps.get_model('user', 'Child') |
||||
for user in User.objects.exclude(child_first_name=''): |
||||
Child.objects.get_or_create(user=user, first_name=user.child_first_name, last_name=user.child_last_name, |
||||
gender=user.child_gender, birthday=user.child_birthday) |
||||
|
||||
class Migration(migrations.Migration): |
||||
|
||||
dependencies = [ |
||||
('user', '0032_child'), |
||||
] |
||||
|
||||
operations = [ |
||||
migrations.RunPython(add_childs), |
||||
] |
||||
@ -0,0 +1,29 @@ |
||||
# Generated by Django 2.0.7 on 2019-06-12 18:52 |
||||
|
||||
from django.db import migrations |
||||
|
||||
|
||||
class Migration(migrations.Migration): |
||||
|
||||
dependencies = [ |
||||
('user', '0033_add_childs'), |
||||
] |
||||
|
||||
operations = [ |
||||
migrations.RemoveField( |
||||
model_name='user', |
||||
name='child_birthday', |
||||
), |
||||
migrations.RemoveField( |
||||
model_name='user', |
||||
name='child_first_name', |
||||
), |
||||
migrations.RemoveField( |
||||
model_name='user', |
||||
name='child_gender', |
||||
), |
||||
migrations.RemoveField( |
||||
model_name='user', |
||||
name='child_last_name', |
||||
), |
||||
] |
||||
@ -1,10 +1,10 @@ |
||||
<div class="section section_video"> |
||||
<div class="section__center center center_sm" style="text-align: center;"> |
||||
<div class="title">Пробный урок</div> |
||||
<img class="main-video-preview js-video-modal" data-video-url="{{ config.MAIN_PAGE_VIDEO_URL }}" data-trial-lesson="1" |
||||
<img class="main-video-preview js-video-modal" data-video-url="{{ config.MAIN_PAGE_VIDEO_URL|safe }}" data-trial-lesson="1" |
||||
src="{{ config.MAIN_PAGE_VIDEO_PREVIEW_IMG.url }}"/> |
||||
<a href="#" class="btn js-video-modal btn_stroke-black" style="margin: 20px;" |
||||
data-video-url="{{ config.MAIN_PAGE_VIDEO_URL }}" data-trial-lesson="1">Смотреть бесплатно</a> |
||||
<div>Много развивающих видео на нашем <a href="{{ config.SERVICE_YOUTUBE_URL }}">YouTube канале</a></div> |
||||
data-video-url="{{ config.MAIN_PAGE_VIDEO_URL|safe }}" data-trial-lesson="1">Смотреть бесплатно</a> |
||||
<div>Много развивающих видео на нашем <a href="{{ config.SERVICE_YOUTUBE_URL|safe }}">YouTube канале</a></div> |
||||
</div> |
||||
</div> |
||||
|
||||
@ -0,0 +1,120 @@ |
||||
<template> |
||||
<div class="user-child-form form__group"> |
||||
<div class="form__title">Карточка ребёнка</div> |
||||
<div class="user-child-form__description"> |
||||
Вся информация конфиденциальна и не передается третьим лицам. Необходима только для персонализации наград, |
||||
поздравлений с Днем Рождения и других персонализированных акций. |
||||
</div> |
||||
<div class="tabs"> |
||||
<div class="user-child-form__tabs tabs__nav"> |
||||
<a class="user-child-form__tab tabs__btn" v-for="(child, index) in childs" :key="index" href="#" |
||||
v-bind:class="{active: selectedIndex == index}" @click.prevent="select(index)"> |
||||
{{ child.first_name ? (child.first_name + ' ' + child.last_name) : ('Ребенок ' + (index || '')) }}</a> |
||||
<a href="#" class="tabs__btn" @click.prevent="add()">Добавить ребенка</a> |
||||
</div> |
||||
</div> |
||||
<div class="tabs__container"> |
||||
<div v-for="(child, index) in childs" :key="index" class="tabs__item js-tabs-item" |
||||
v-bind:style="{display: selectedIndex == index ? 'block' : ''}"> |
||||
<div class="form__fieldset"> |
||||
<input type="hidden" name="child_id" :value="child.id" /> |
||||
<div class="form__field field" v-bind:class="{error: child.errors && child.errors.gender}"> |
||||
<div class="field__label">ИМЯ РЕБЕНКА</div> |
||||
<div class="field__wrap"> |
||||
<input name='child_first_name' class="field__input" type="text" placeholder="Имя" |
||||
v-model="child.first_name"> |
||||
</div> |
||||
<div v-if="child.errors && child.errors.first_name" class="field__error">{{ child.errors.first_name }}</div> |
||||
</div> |
||||
<div class="form__field field" v-bind:class="{error: child.errors && child.errors.last_name}"> |
||||
<div class="field__label">ФАМИЛИЯ РЕБЕНКА</div> |
||||
<div class="field__wrap"> |
||||
<input name='child_last_name' id="child-last-name" class="field__input" type="text" placeholder="Фамилия" |
||||
v-model="child.last_name"> |
||||
</div> |
||||
<div v-if="child.errors && child.errors.last_name" class="field__error">{{ child.errors.last_name }}</div> |
||||
</div> |
||||
</div> |
||||
<div class="form__fieldset"> |
||||
<div class="form__field field" v-bind:class="{error: child.errors && child.errors.birthday}"> |
||||
<div class="field__label">ДАТА РОЖДЕНИЯ</div> |
||||
<div class="field__wrap"> |
||||
<vue-datepicker input-class="field__input" name="child_birthday" language="ru" format="dd/MM/yyyy" |
||||
v-model="child.birthday" placeholder="dd/mm/yyyy"/> |
||||
</div> |
||||
<div v-if="child.errors && child.errors.birthday" class="field__error">{{ child.errors.birthday }}</div> |
||||
</div> |
||||
<div class="form__field field" v-bind:class="{error: child.errors && child.errors.gender}"> |
||||
<div class="field__label">ПОЛ</div> |
||||
<div class="field__wrap"> |
||||
<lil-select :value.sync="child.gender" :options="genders" value-key="0" title-key="1"/> |
||||
<input name='child_gender' type="hidden" v-model="child.gender"> |
||||
</div> |
||||
<div v-if="child.errors && child.errors.gender" class="field__error">{{ child.errors.gender }}</div> |
||||
</div> |
||||
</div> |
||||
<a href="#" @click.prevent="remove(index)">Удалить</a> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import DatePicker from 'vuejs-datepicker'; |
||||
import LilSelect from './inputs/LilSelect'; |
||||
|
||||
export default { |
||||
name: "childs", |
||||
props: ['childs'], |
||||
data() { |
||||
return { |
||||
selectedIndex: 0, |
||||
selectedChild: null, |
||||
genders: [ |
||||
['n', 'М / Ж'], |
||||
['f', 'Ж'], |
||||
['m', 'М'], |
||||
] |
||||
} |
||||
}, |
||||
methods: { |
||||
add(){ |
||||
this.childs.push({ |
||||
first_name: '', |
||||
last_name: '', |
||||
birthday: '', |
||||
gender: 'n', |
||||
errors: {}, |
||||
}); |
||||
this.select(this.childs.length - 1); |
||||
}, |
||||
remove(index){ |
||||
if(! confirm('Вы действительно хотите удалить данные о ребенке?')){ |
||||
return; |
||||
} |
||||
this.childs.splice(index, 1); |
||||
if(! this.childs.length){ |
||||
this.add(); |
||||
} |
||||
else if(this.selectedIndex == index){ |
||||
this.select(0); |
||||
} |
||||
}, |
||||
select(index){ |
||||
this.selectedIndex = index; |
||||
this.selectedChild = this.childs[index]; |
||||
} |
||||
}, |
||||
mounted(){ |
||||
for(let i in this.childs){ |
||||
if(this.childs[i].errors){ |
||||
this.select(i); |
||||
} |
||||
} |
||||
}, |
||||
components: { |
||||
'vue-datepicker': DatePicker, |
||||
LilSelect, |
||||
} |
||||
} |
||||
</script> |
||||
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 93 KiB |
@ -0,0 +1,62 @@ |
||||
import $ from 'jquery'; |
||||
import {loadScript} from '../utils'; |
||||
|
||||
function getVideoPopup($videoEl){ |
||||
const $container = $videoEl.parent(); |
||||
let $popup = $container.children('.video-ended-popup'); |
||||
if(! $popup[0]){ |
||||
$popup = $('<div class="video-ended-popup">' + |
||||
'<div class="loading-loader"></div>' + |
||||
'<div class="video-ended-popup__like-btn"></div></div>'); |
||||
$container.append($popup); |
||||
} |
||||
return $popup; |
||||
} |
||||
|
||||
$(document).ready(function () { |
||||
$('.js-video').each(function (){ |
||||
const $iframe = $(this); |
||||
if($iframe.data('isYoutube')){ |
||||
|
||||
} |
||||
if($iframe.data('isVimeo')){ |
||||
loadScript('https://player.vimeo.com/api/player.js').then(() => { |
||||
const player = new Vimeo.Player(this); |
||||
player.on('pause', function(data) { |
||||
if(data.percent == 1){ |
||||
const $popup = getVideoPopup($iframe); |
||||
$popup.show().animate({opacity: 1}, 200); |
||||
if (document.fullscreenElement){ |
||||
document.exitFullscreen(); |
||||
} |
||||
const courseId = $iframe.data('courseId'); |
||||
if(courseId && window.LIL_STORE.user.id && !window.LIL_STORE.data.courseLiked){ |
||||
$popup.addClass('video-ended-popup_loading'); |
||||
$.get('/api/v1/likes/course-liked/', { |
||||
course_id: courseId, |
||||
user_id: window.LIL_STORE.user.id |
||||
}).then(response => { |
||||
if(! response.is_liked){ |
||||
$popup.addClass('video-ended-popup_like'); |
||||
$popup.find('.video-ended-popup__like-btn').click(() => { |
||||
$.post(`/course/${courseId}/like`).then(response => { |
||||
$popup.removeClass('video-ended-popup_like'); |
||||
window.LIL_STORE.data.courseLiked = response.success; |
||||
}); |
||||
}); |
||||
} |
||||
$popup.removeClass('video-ended-popup_loading'); |
||||
}); |
||||
} |
||||
} |
||||
}); |
||||
player.on('play', function() { |
||||
const $p = getVideoPopup($iframe); |
||||
if($p.is(':visible')){ |
||||
$p.animate({opacity: 0}, 800).then(() => {$p.hide().attr('class', 'video-ended-popup');}); |
||||
} |
||||
}); |
||||
}); |
||||
} |
||||
}); |
||||
}); |
||||
@ -0,0 +1 @@ |
||||
import "../modules/content"; |
||||
@ -0,0 +1 @@ |
||||
import "../modules/content"; |
||||
@ -1,14 +1,21 @@ |
||||
export const rupluralize = (value, args, addValue=true) => { |
||||
let digit = Math.trunc(value) + ''; |
||||
digit = digit[digit.length - 1]; |
||||
return (addValue ? value + ' ' : '') + |
||||
args[(+value > 10 && +value < 20) |
||||
? 2 |
||||
: (digit == '1' ? 0 : ('234'.search(digit) > -1 ? 1 : 2))]; |
||||
} |
||||
export const rupluralize = (value, args, addValue = true) => { |
||||
let digit = Math.trunc(value) + ''; |
||||
digit = digit[digit.length - 1]; |
||||
return (addValue ? value + ' ' : '') + |
||||
args[(+value > 10 && +value < 20) |
||||
? 2 |
||||
: (digit == '1' ? 0 : ('234'.search(digit) > -1 ? 1 : 2))]; |
||||
}; |
||||
|
||||
export const loadScript = (url, onload) => { |
||||
const tag = document.createElement('script'); |
||||
tag.url = url; |
||||
document.getElementsByTagName('body'); |
||||
} |
||||
return new Promise(resolve => { |
||||
const script = document.createElement('script'); |
||||
script.async = true; |
||||
document.body.appendChild(script); |
||||
script.onload = function () { |
||||
onload && onload.apply(this, arguments); |
||||
resolve.apply(this, arguments); |
||||
}; |
||||
script.src = url; |
||||
}); |
||||
}; |
||||
|
||||
Loading…
Reference in new issue