Merge branch 'master' into production

This commit is contained in:
Matt Lott 2016-06-09 10:48:02 -07:00
commit c85a7230a6
22 changed files with 228 additions and 226 deletions

View file

@ -95,9 +95,13 @@ module.exports = class Tracker extends CocoClass
analytics.identify me.id, traits
trackPageView: (includeIntegrations=[]) ->
includeMixpanel = (name) ->
mixpanelIncludes = ['', 'schools', 'play', 'play/level/dungeons-of-kithgard']
name in mixpanelIncludes or /courses|students|teachers/ig.test(name)
name = Backbone.history.getFragment()
url = "/#{name}"
console.log "Would track analytics pageview: #{url}" if debugAnalytics
console.log "Would track analytics pageview: #{url} Mixpanel=#{includeMixpanel(name)}" if debugAnalytics
@trackEventInternal 'Pageview', url: name unless me?.isAdmin() and @isProduction
return unless @isProduction and not me.isAdmin()
@ -106,8 +110,7 @@ module.exports = class Tracker extends CocoClass
ga? 'send', 'pageview', url
# Mixpanel
mixpanelIncludes = ['', 'courses', 'courses/purchase', 'courses/teachers', 'courses/students', 'schools', 'teachers', 'teachers/freetrial', 'teachers/quote', 'play', 'play/level/dungeons-of-kithgard']
mixpanel.track('page viewed', 'page name' : name, url : url) if name in mixpanelIncludes
mixpanel.track('page viewed', 'page name' : name, url : url) if includeMixpanel(name)
if me.isTeacher() and @segmentLoaded
options = {}

View file

@ -67,7 +67,7 @@ block content
h5
span.spr= classroom.get('name')
span.spr (#{(classroom.get('aceConfig') || {}).language === 'javascript' ? 'JavaScript' : 'Python'})
a(href="/courses/"+classroom.id, data-i18n="courses.view_class")
a.view-class-btn(data-classroom-id=classroom.id, data-i18n="courses.view_class")
- var courseInstances = view.courseInstances.where({classroomID: classroom.id});
for courseInstance in courseInstances
@ -77,7 +77,7 @@ block content
h6
span.spr= course.get('name')
small
a(href="/courses/"+courseInstance.get('courseID')+'/'+courseInstance.id, data-i18n="courses.view_levels")
a.view-levels-btn(data-course-id=courseInstance.get('courseID'), data-courseinstance-id=courseInstance.id, data-i18n="courses.view_levels")
+course-instance-body(courseInstance, classroom)
.clearfix
@ -112,19 +112,19 @@ mixin course-instance-body(courseInstance, classroom)
- var arenaLevel = stats.levels.arena;
if arenaLevel
- var arenaURL = "/play/ladder/"+arenaLevel.get('slug')+"/course/"+courseInstance.id;
a.btn.btn-burgandy.btn-lg.m-b-1(href=arenaURL)
a.play-btn.btn.btn-burgandy.btn-lg.m-b-1(data-href=arenaURL, data-level-slug=arenaLevel.get('slug'), data-event-action="Students Play Arena")
span(data-i18n="courses.play_arena")
else
a.btn.btn-default.btn-lg.m-b-1(disabled=true, data-i18n="courses.course_complete")
else if stats.levels.next != stats.levels.first
- var next = stats.levels.next;
- var levelURL = "/play/level/"+next.get('slug')+"?course="+courseInstance.get('courseID')+"&course-instance="+courseInstance.id;
a.btn.btn-forest.btn-lg.m-b-1(href=levelURL)
a.play-btn.btn.btn-forest.btn-lg.m-b-1(data-href=levelURL, data-level-slug=next.get('slug'), data-event-action="Students Continue Course")
span(data-i18n="common.continue")
else
- var firstLevel = stats.levels.first;
- var levelURL = "/play/level/"+firstLevel.get('slug')+"?course="+courseInstance.get('courseID')+"&course-instance="+courseInstance.id;
a.btn.btn-navy.btn-lg.m-b-1(href=levelURL)
a.play-btn.btn.btn-navy.btn-lg.m-b-1(data-href=levelURL, data-level-slug=firstLevel.get('slug'), data-event-action="Students Start Course")
span(data-i18n="courses.start")
div

View file

@ -244,7 +244,7 @@ mixin studentRow(student)
div(data-i18n='teacher.remove')
mixin enrollStudentButton(student)
a.enroll-student-button.btn.btn-lg.btn-primary(data-classroom-id=view.classroom.id data-user-id=student.id)
a.enroll-student-button.btn.btn-lg.btn-primary(data-classroom-id=view.classroom.id data-user-id=student.id data-event-action="Teachers Class Students Enroll Student")
span(data-i18n='teacher.enroll_student')
mixin courseProgressTab
@ -294,7 +294,7 @@ mixin courseProgressTab
span(data-i18n='TODO')
| Assign Course
else
.enroll-student-button.btn.btn-md.btn-navy.pull-right(data-user-id=student.id)
.enroll-student-button.btn.btn-md.btn-navy.pull-right(data-user-id=student.id data-event-action="Teachers Class Course Enroll Student")
span(data-i18n='TODO')
| Enroll Student
@ -426,4 +426,4 @@ mixin enrollmentStatusTab
strong(class= status === 'expired' ? 'text-danger' : '')= view.studentStatusString(student)
td.enroll-col
if status !== 'enrolled'
button.enroll-student-button.btn.btn-navy(data-i18n="teacher.enroll_student", data-user-id=student.id)
button.enroll-student-button.btn.btn-navy(data-i18n="teacher.enroll_student", data-user-id=student.id, data-event-action="Teachers Class Enrollment Enroll Student")

View file

@ -10,15 +10,15 @@ block content
p(data-i18n='teacher.teacher_account_required')
if me.isAnonymous()
.login-button.btn.btn-lg.btn-primary(data-i18n='login.log_in')
a.btn.btn-lg.btn-primary-alt(href="/teachers/signup" data-i18n='teacher.create_teacher_account')
button.btn.btn-lg.btn-primary-alt.create-teacher-btn(data-event-action="Teachers Classes Create Teacher Account", data-i18n='teacher.create_teacher_account')
else
a.btn.btn-lg.btn-primary(href="/teachers/update-account" data-i18n="teachers_quote.convert_account_title")
button.btn.btn-lg.btn-primary.update-teacher-btn(data-event-action="Teachers Classes Convert Teacher Account", data-i18n="teachers_quote.convert_account_title")
button#logout-button.btn.btn-lg.btn-primary-alt(data-i18n="login.log_out")
.teacher-account-blurb.text-center.col-xs-6.col-xs-offset-3.m-y-3
h5(data-i18n='teacher.what_is_a_teacher_account')
p(data-i18n='teacher.teacher_account_explanation')
else
if !me.isTeacher()
.alert.alert-danger.text-center
@ -27,30 +27,30 @@ block content
h3 ATTENTION: Please upgrade your account to a Teacher Account.
p
| We are transitioning to a new improved classroom management system for instructors.
| Please convert your account to ensure you retain access to your classrooms.
a.btn.btn-primary.btn-lg(href="/teachers/update-account") Upgrade to teacher account
| Please convert your account to ensure you retain access to your classrooms.
button.btn.btn-primary.btn-lg.update-teacher-btn(data-event-action="Teachers Classes Convert Teacher Account Temp") Upgrade to teacher account
.container
h3(data-i18n='teacher.current_classes')
.classes.container
// Loop each class
each classroom in view.classrooms.models
unless classroom.get('archived')
+classRow(classroom)
+createClassButton
- var archivedClassrooms = view.classrooms.where({archived: true});
if _.size(archivedClassrooms)
.container
h3(data-i18n='teacher.archived_classes')
p(data-i18n='teacher.archived_classes_blurb')
.classes.container
each classroom in archivedClassrooms
+archivedClassRow(classroom)
mixin classRow(classroom)
.class.row
.col-xs-6
@ -67,9 +67,9 @@ mixin classRow(classroom)
span
= classroom.get('members').length
.class-links
a.text-h6(data-i18n='teacher.view_class' href=('/teachers/classes/' + classroom.id))
a.edit-classroom.text-h6(data-i18n='teacher.edit_class_settings' data-classroom-id=classroom.id)
a.archive-classroom.text-h6(data-i18n='teacher.archive_class' data-classroom-id=classroom.id)
a.view-class-btn.text-h6(data-i18n='teacher.view_class' data-classroom-id=classroom.id data-event-action="Teachers Classes View Class Link")
a.edit-classroom.text-h6(data-i18n='teacher.edit_class_settings' data-classroom-id=classroom.id data-event-action="Teachers Classes Edit Class Started")
a.archive-classroom.text-h6(data-i18n='teacher.archive_class' data-classroom-id=classroom.id data-event-action="Teachers Classes Archive Class")
.progress-col.col-xs-5
if classroom.get('members').length == 0
@ -80,7 +80,7 @@ mixin classRow(classroom)
if view.courseInstances.findWhere({ classroomID: classroom.id, courseID: course.id })
+progressDot(classroom, course, index)
.view-class-arrow.col-xs-1
a.view-class-arrow-inner.glyphicon.glyphicon-chevron-right(data-classroom-id=classroom.id, href=('/teachers/classes/' + classroom.id))
a.view-class-arrow-inner.glyphicon.glyphicon-chevron-right.view-class-btn(data-classroom-id=classroom.id data-event-action="Teachers Classes View Class Chevron")
mixin addStudentsButton(classroom)
@ -91,7 +91,7 @@ mixin addStudentsButton(classroom)
a.add-students-btn.btn.btn-lg.btn-primary(data-classroom-id=classroom.id )
span(data-i18n='teacher.add_students')
| Add Students
mixin createClassButton
.create-class
.text-center
@ -110,7 +110,7 @@ mixin progressDot(classroom, course, index)
- complete = courseInstance.numCompleted
- started = courseInstance.started
- dotClass = complete === total ? 'forest' : started ? 'gold' : '';
- var progressDotContext = {total: total, complete: complete};
- var progressDotContext = {total: total, complete: complete};
.progress-dot(class=dotClass, data-title=view.progressDotTemplate(progressDotContext))
+progressDotLabel(index)

View file

@ -86,10 +86,10 @@ mixin course-info(course)
if view.guideLinks[course.id]
//- a.btn.btn-primary(href=view.guideLinks[course.id] class=(me.isTeacher() ? '': 'disabled'))
//- span(data-i18n="courses.print_guide")
a.btn.btn-primary(href=view.guideLinks[course.id].python class=(me.isTeacher() ? '': 'disabled'))
a.guide-btn.btn.btn-primary(href=view.guideLinks[course.id].python data-course-id=course.id data-course-name=course.get('name') data-event-action="Classes Guides Guide Python" class=(me.isTeacher() ? '': 'disabled'))
span(data-i18n="courses.view_guide_online")
| — Python
a.btn.btn-primary(href=view.guideLinks[course.id].javascript class=(me.isTeacher() ? '': 'disabled'))
a.guide-btn.btn.btn-primary(href=view.guideLinks[course.id].javascript data-course-id=course.id data-course-name=course.get('name') data-event-action="Classes Guides Guide JavaScript" class=(me.isTeacher() ? '': 'disabled'))
span(data-i18n="courses.view_guide_online")
| — JavaScript
else

View file

@ -5,36 +5,36 @@ mixin box
if me.isAnonymous() == true
h6#classroom-edition-header(data-i18n="new_home.classroom_edition")
div
button.teacher-btn.btn.btn-primary.btn-lg.btn-block(data-i18n="new_home.im_a_teacher")
a.teacher-btn.btn.btn-primary.btn-lg.btn-block(data-event-action="Homepage Click Teacher Button CTA", data-i18n="new_home.im_a_teacher")
div
a.btn.btn-forest.btn-lg.btn-block(href="/courses", data-i18n="new_home.im_a_student")
a.student-btn.btn.btn-forest.btn-lg.btn-block(href="/courses", data-event-action="Homepage Click Student Button CTA", data-i18n="new_home.im_a_student")
h6#learn-to-code-header(data-i18n="new_home.learn_to_code")
a.btn.btn-gold.btn-lg.btn-block.play-btn(href=view.playURL, data-i18n="new_home.play_now")
a.btn.btn-gold.btn-lg.btn-block.play-btn(href=view.playURL, data-event-action="Homepage Play Now CTA", data-i18n="new_home.play_now")
else
h6#classroom-edition-header(data-i18n="new_home.logged_in_as")
p.small #{me.get("email")}
if me.isTeacher()
div
button.teacher-btn.btn.btn-forest.btn-lg.btn-block(data-i18n="new_home.goto_classes")
button.teacher-btn.btn.btn-forest.btn-lg.btn-block(data-event-action="Homepage Click My Classes CTA", data-i18n="new_home.goto_classes")
div
if view.isTeacherWithDemo
h6(data-i18n="new_home.check_out_wiki")
a.btn.btn-primary.btn-lg.btn-block(href="https://sites.google.com/a/codecombat.com/teacher-guides/course-guides", data-i18n="nav.educator_wiki")
button.wiki-btn.btn.btn-primary.btn-lg.btn-block(data-event-action="Homepage Click Educator Wiki CTA", data-i18n="nav.educator_wiki")
else
h6(data-i18n="new_home.want_coco")
a.btn.btn-primary.btn-lg.btn-block(href=view.demoRequestURL, data-i18n="new_home.get_started")
button.btn.btn-primary.btn-lg.request-demo(data-event-action="Homepage Request Demo CTA", data-i18n="new_home.request_demo")
else if me.justPlaysCourses()
div
a.btn.btn-forest.btn-lg.btn-block(href=view.playURL, data-i18n="courses.continue_playing")
a.btn.btn-forest.btn-lg.btn-block.play-btn(href=view.playURL, data-event-action="Homepage Classroom Continue Playing CTA", data-i18n="courses.continue_playing")
div
a.btn.btn-primary.btn-lg.btn-block.play-btn(href=view.playURL, data-i18n="new_home.view_progress")
a.btn.btn-primary.btn-lg.btn-block.play-btn(href=view.playURL, data-event-action="Homepage View Progress CTA", data-i18n="new_home.view_progress")
else
div
a.btn.btn-forest.btn-lg.btn-block.play-btn(href=view.playURL, data-i18n="courses.continue_playing")
a.btn.btn-forest.btn-lg.btn-block.play-btn(href=view.playURL, data-event-action="Homepage Campaign Continue Playing CTA", data-i18n="courses.continue_playing")
div
a.btn.btn-primary.btn-lg.btn-block(href="/user/#{me.getSlugOrID()}", data-i18n="new_home.view_profile")
a.btn.btn-primary.btn-lg.btn-block.profile-btn(href=view.playURL, data-event-action="Homepage View Profile CTA", data-i18n="new_home.view_profile")
p.small
@ -213,11 +213,11 @@ block content
if view.isTeacherWithDemo
h4(data-i18n="new_home.get_started_subtitle")
div
a.btn.btn-primary.btn-lg(href="/teachers/classes", data-i18n="new_home.setup_a_class")
button.btn.btn-primary.btn-lg.setup-class-btn(data-event-action="Homepage Setup Class", data-i18n="new_home.setup_a_class")
else
h4(data-i18n="new_home.request_demo_subtitle")
div
a.btn.btn-primary.btn-lg(href=view.demoRequestURL, data-i18n="new_home.request_demo")
button.btn.btn-primary.btn-lg.request-demo(data-event-action="Homepage Request Demo", data-i18n="new_home.request_demo")
if me.isAnonymous()
.have-an-account
span.spr(data-i18n="new_home.have_an_account")
@ -306,10 +306,10 @@ block content
h3(data-i18n="new_home.run_class")
if view.isTeacherWithDemo
div
a.btn.btn-primary.btn-lg(href="/teachers/classes", data-i18n="new_home.setup_a_class")
button.btn.btn-primary.btn-lg.setup-class-btn(data-event-action="Homepage Setup Class Page Bottom", data-i18n="new_home.setup_a_class")
else
div
a.btn.btn-primary.btn-lg(href=view.demoRequestURL, data-i18n="new_home.request_demo")
button.btn.btn-primary.btn-lg.request-demo(data-event-action="Homepage Request Demo Page Bottom", data-i18n="new_home.request_demo")
if me.isAnonymous()
.have-an-account
span.spr(data-i18n="new_home.have_an_account")

View file

@ -18,14 +18,17 @@ module.exports = class NewHomeView extends RootView
events:
'click .play-btn': 'onClickPlayButton'
'change #school-level-dropdown': 'onChangeSchoolLevelDropdown'
'click .student-btn': 'onClickStudentButton'
'click .teacher-btn': 'onClickTeacherButton'
'click #learn-more-link': 'onClickLearnMoreLink'
'click .screen-thumbnail': 'onClickScreenThumbnail'
'click #carousel-left': 'onLeftPressed'
'click #carousel-right': 'onRightPressed'
'click .request-demo': 'onClickRequestDemo'
'click .join-class': 'onClickJoinClass'
'click .logout-btn': 'logoutAccount'
'click .profile-btn': 'onClickViewProfile'
'click .setup-class-btn': 'onClickSetupClass'
'click .wiki-btn': 'onClickWikiButton'
shortcuts:
'right': 'onRightPressed'
@ -36,7 +39,6 @@ module.exports = class NewHomeView extends RootView
@courses = new CocoCollection [], {url: "/db/course", model: Course}
@supermodel.loadCollection(@courses, 'courses')
window.tracker?.trackEvent 'Homepage Loaded', category: 'Homepage'
if me.isTeacher()
@trialRequests = new TrialRequests()
@trialRequests.fetchOwn()
@ -51,43 +53,52 @@ module.exports = class NewHomeView extends RootView
else if me.justPlaysCourses()
# Save players who might be in a classroom from getting into the campaign
@playURL = '/courses'
@alternatePlayURL = '/play'
@alternatePlayText = 'home.play_campaign_version'
else
@playURL = '/play'
onLoaded: ->
@trialRequest = @trialRequests.first() if @trialRequests?.size()
@isTeacherWithDemo = @trialRequest and @trialRequest.get('status') in ['approved', 'submitted']
@demoRequestURL = if me.isTeacher() then '/teachers/update-account' else '/teachers/demo'
super()
onClickLearnMoreLink: ->
window.tracker?.trackEvent 'Homepage Click Learn More', category: 'Homepage', ['Mixpanel']
@scrollToLink('#classroom-in-box-container')
onClickPlayButton: (e) ->
@playSound 'menu-button-click'
e.preventDefault()
e.stopImmediatePropagation()
window.tracker?.trackEvent 'Homepage Click Play', category: 'Homepage'
application.router.navigate @playURL, trigger: true
#window.open @playURL, '_blank'
window.tracker?.trackEvent $(e.target).data('event-action'), category: 'Homepage', ['Mixpanel']
onClickRequestDemo: (e) ->
@playSound 'menu-button-click'
e.preventDefault()
e.stopImmediatePropagation()
window.tracker?.trackEvent 'Homepage Submit Jumbo Form', category: 'Homepage'
obj = storage.load('request-quote-form')
obj ?= {}
obj.role = @$('#request-form-role').val()
obj.numStudents = @$('#request-form-range').val()
storage.save('request-quote-form', obj)
application.router.navigate "/teachers/demo", trigger: true
window.tracker?.trackEvent $(e.target).data('event-action'), category: 'Homepage', ['Mixpanel']
if me.isTeacher()
application.router.navigate '/teachers/update-account', trigger: true
else
application.router.navigate '/teachers/demo', trigger: true
onClickJoinClass: (e) ->
@playSound 'menu-button-click'
e.preventDefault()
e.stopImmediatePropagation()
window.tracker?.trackEvent 'Homepage Click Join Class', category: 'Homepage'
application.router.navigate "/courses", trigger: true
onClickSetupClass: (e) ->
window.tracker?.trackEvent $(e.target).data('event-action'), category: 'Homepage', ['Mixpanel']
application.router.navigate("/teachers/classes", { trigger: true })
onClickStudentButton: (e) ->
window.tracker?.trackEvent $(e.target).data('event-action'), category: 'Homepage', ['Mixpanel']
onClickTeacherButton: (e) ->
window.tracker?.trackEvent $(e.target).data('event-action'), category: 'Homepage', ['Mixpanel']
if me.isTeacher()
application.router.navigate('/teachers', { trigger: true })
else
@scrollToLink('.request-demo-row', 600)
onClickViewProfile: (e) ->
window.tracker?.trackEvent $(e.target).data('event-action'), category: 'Homepage', ['Mixpanel']
application.router.navigate("/user/#{me.getSlugOrID()}", { trigger: true })
onClickWikiButton: (e) ->
window.tracker?.trackEvent $(e.target).data('event-action'), category: 'Homepage', ['Mixpanel']
window.location.href = 'https://sites.google.com/a/codecombat.com/teacher-guides/course-guides'
afterRender: ->
@onChangeSchoolLevelDropdown()
@ -125,18 +136,6 @@ module.exports = class NewHomeView extends RootView
isNewPlayer: ->
not me.get('stats')?.gamesCompleted and not me.get('heroConfig')
onClickLearnMoreLink: ->
window.tracker?.trackEvent 'Homepage Click Learn More', category: 'Homepage'
@scrollToLink('#classroom-in-box-container')
onClickTeacherButton: ->
if me.isTeacher()
window.tracker?.trackEvent 'Homepage Click Teacher Button (logged in)', category: 'Homepage'
application.router.navigate('/teachers', { trigger: true })
else
window.tracker?.trackEvent 'Homepage Click Teacher Button', category: 'Homepage'
@scrollToLink('.request-demo-row', 600)
onRightPressed: (event) ->
# Special handling, otherwise after you click the control, keyboard presses move the slide twice
return if event.type is 'keydown' and $(document.activeElement).is('.carousel-control')

View file

@ -14,10 +14,6 @@ module.exports = class ClassroomSettingsModal extends ModalView
initialize: (options={}) ->
@classroom = options.classroom or new Classroom()
if @classroom.isNew()
application.tracker?.trackEvent 'Create new class', category: 'Courses'
else
application.tracker?.trackEvent 'Classroom started edit settings', category: 'Courses', classroomID: @classroom.id
afterRender: ->
super()
@ -53,3 +49,5 @@ module.exports = class ClassroomSettingsModal extends ModalView
button.text(@oldButtonText).attr('disabled', false)
errors.showNotyNetworkError(jqxhr)
@listenToOnce @classroom, 'sync', @hide
window.tracker?.trackEvent "Teachers Edit Class Saved", category: 'Teachers', classroomID: @classroom.id, ['Mixpanel']

View file

@ -57,8 +57,8 @@ module.exports = class ClassroomView extends RootView
@levels = new Levels()
@levels.fetchForClassroom(classroomID, {data: {project: 'name,slug,original'}})
@levels.on 'add', (model) -> @_byId[model.get('original')] = model # so you can 'get' them
@supermodel.trackCollection(@levels)
window.tracker?.trackEvent 'Students Class Loaded', category: 'Students', classroomID: classroomID, ['Mixpanel']
onCourseInstancesSync: ->
@sessions = new CocoCollection([], { model: LevelSession })

View file

@ -74,6 +74,10 @@ module.exports = class CourseDetailsView extends RootView
))
))
initialize: (options) ->
window.tracker?.trackEvent 'Students Class Course Loaded', category: 'Students', ['Mixpanel']
super(options)
buildSessionStats: ->
return if @destroyed
@ -125,6 +129,7 @@ module.exports = class CourseDetailsView extends RootView
levelSlug = $(e.target).closest('.btn-play-level').data('level-slug')
levelID = $(e.target).closest('.btn-play-level').data('level-id')
level = @levels.findWhere({original: levelID})
window.tracker?.trackEvent 'Students Class Course Play Level', category: 'Students', courseID: @courseID, courseInstanceID: @courseInstanceID, levelSlug: levelSlug, ['Mixpanel']
if level.get('type') is 'course-ladder'
viewClass = 'views/ladder/LadderView'
viewArgs = [{supermodel: @supermodel}, levelSlug]

View file

@ -29,11 +29,14 @@ module.exports = class CoursesView extends RootView
'click .change-hero-btn': 'onClickChangeHeroButton'
'click #join-class-btn': 'onClickJoinClassButton'
'submit #join-class-form': 'onSubmitJoinClassForm'
'click #change-language-link': 'onClickChangeLanguageLink'
'click .play-btn': 'onClickPlay'
'click .view-class-btn': 'onClickViewClass'
'click .view-levels-btn': 'onClickViewLevels'
getTitle: -> return $.i18n.t('teacher.students')
initialize: ->
@classCodeQueryVar = utils.getQueryVariable('_cc', false)
@courseInstances = new CocoCollection([], { url: "/db/user/#{me.id}/course_instances", model: CourseInstance})
@courseInstances.comparator = (ci) -> return ci.get('classroomID') + ci.get('courseID')
@listenToOnce @courseInstances, 'sync', @onCourseInstancesLoaded
@ -55,6 +58,7 @@ module.exports = class CoursesView extends RootView
@supermodel.loadModel(@hero, 'hero')
@listenTo @hero, 'all', ->
@render()
window.tracker?.trackEvent 'Students Loaded', category: 'Students', ['Mixpanel']
onCourseInstancesLoaded: ->
map = {}
@ -76,20 +80,22 @@ module.exports = class CoursesView extends RootView
onLoaded: ->
super()
if utils.getQueryVariable('_cc', false) and not me.isAnonymous()
if @classCodeQueryVar and not me.isAnonymous()
window.tracker?.trackEvent 'Students Join Class Link', category: 'Students', classCode: @classCodeQueryVar, ['Mixpanel']
@joinClass()
onClickLogInButton: ->
modal = new AuthModal()
@openModalView(modal)
application.tracker?.trackEvent 'Started Student Login', category: 'Courses'
window.tracker?.trackEvent 'Students Login Started', category: 'Students', ['Mixpanel']
openSignUpModal: ->
window.tracker?.trackEvent 'Students Signup Started', category: 'Students', ['Mixpanel']
modal = new CreateAccountModal({ initialValues: { classCode: utils.getQueryVariable('_cc', "") } })
@openModalView(modal)
application.tracker?.trackEvent 'Started Student Signup', category: 'Courses'
onClickChangeHeroButton: ->
window.tracker?.trackEvent 'Students Change Hero Started', category: 'Students', ['Mixpanel']
modal = new HeroSelectModal({ currentHeroID: @hero.id })
@openModalView(modal)
@listenTo modal, 'hero-select:success', (newHero) =>
@ -101,16 +107,20 @@ module.exports = class CoursesView extends RootView
onSubmitJoinClassForm: (e) ->
e.preventDefault()
classCode = @$('#class-code-input').val() or @classCodeQueryVar
window.tracker?.trackEvent 'Students Join Class With Code', category: 'Students', classCode: classCode, ['Mixpanel']
@joinClass()
onClickJoinClassButton: (e) ->
classCode = @$('#class-code-input').val() or @classCodeQueryVar
window.tracker?.trackEvent 'Students Join Class With Code', category: 'Students', classCode: classCode, ['Mixpanel']
@joinClass()
joinClass: ->
return if @state
@state = 'enrolling'
@errorMessage = null
@classCode = @$('#class-code-input').val() or utils.getQueryVariable('_cc', false)
@classCode = @$('#class-code-input').val() or @classCodeQueryVar
if not @classCode
@state = null
@errorMessage = 'Please enter a code.'
@ -133,7 +143,6 @@ module.exports = class CoursesView extends RootView
onJoinClassroomError: (classroom, jqxhr, options) ->
@state = null
application.tracker?.trackEvent 'Failed to join classroom with code', category: 'Courses', status: jqxhr.status
if jqxhr.status is 422
@errorMessage = 'Please enter a code.'
else if jqxhr.status is 404
@ -161,9 +170,19 @@ module.exports = class CoursesView extends RootView
# TODO: Smoother system for joining a classroom and course instances, without requiring page reload,
# and showing which class was just joined.
document.location.search = '' # Using document.location.reload() causes an infinite loop of reloading
onClickChangeLanguageLink: ->
application.tracker?.trackEvent 'Student clicked change language', category: 'Courses'
modal = new ChangeCourseLanguageModal()
@openModalView(modal)
modal.once 'hidden', @render, @
onClickPlay: (e) ->
levelSlug = $(e.currentTarget).data('level-slug')
window.tracker?.trackEvent $(e.currentTarget).data('event-action'), category: 'Students', levelSlug: levelSlug, ['Mixpanel']
application.router.navigate($(e.currentTarget).data('href'), { trigger: true })
onClickViewClass: (e) ->
classroomID = $(e.target).data('classroom-id')
window.tracker?.trackEvent 'Students View Class', category: 'Students', classroomID: classroomID, ['Mixpanel']
application.router.navigate("/courses/#{classroomID}", { trigger: true })
onClickViewLevels: (e) ->
courseID = $(e.target).data('course-id')
courseInstanceID = $(e.target).data('courseinstance-id')
window.tracker?.trackEvent 'Students View Levels', category: 'Students', courseID: courseID, courseInstanceID: courseInstanceID, ['Mixpanel']
application.router.navigate("/courses/#{courseID}/#{courseInstanceID}", { trigger: true })

View file

@ -21,7 +21,7 @@ module.exports = class EnrollmentsView extends RootView
getTitle: -> return $.i18n.t('teacher.enrollments')
initialize: ->
initialize: (options) ->
@state = new State({
totalEnrolled: 0
totalNotEnrolled: 0
@ -34,6 +34,8 @@ module.exports = class EnrollmentsView extends RootView
'pending': []
}
})
window.tracker?.trackEvent 'Classes Licenses Loaded', category: 'Teachers', ['Mixpanel']
super(options)
@courses = new Courses()
@supermodel.trackRequest @courses.fetch({data: { project: 'free' }})
@ -58,31 +60,31 @@ module.exports = class EnrollmentsView extends RootView
@calculateEnrollmentStats()
@state.set('totalCourses', @courses.size())
super()
updatePrepaidGroups: ->
@state.set('prepaidGroups', @prepaids.groupBy((p) -> p.status()))
calculateEnrollmentStats: ->
@removeDeletedStudents()
# sort users into enrolled, not enrolled
groups = @members.groupBy (m) -> m.isEnrolled()
enrolledUsers = new Users(groups.true)
@notEnrolledUsers = new Users(groups.false)
map = {}
map = {}
for classroom in @classrooms.models
map[classroom.id] = _.countBy(classroom.get('members'), (userID) -> enrolledUsers.get(userID)?).false
@state.set({
totalEnrolled: enrolledUsers.size()
totalNotEnrolled: @notEnrolledUsers.size()
classroomNotEnrolledMap: map
})
true
removeDeletedStudents: (e) ->
for classroom in @classrooms.models
_.remove(classroom.get('members'), (memberID) =>
@ -94,8 +96,9 @@ module.exports = class EnrollmentsView extends RootView
@openModalView(new HowToEnrollModal())
onClickContactUsButton: ->
window.tracker?.trackEvent 'Classes Licenses Contact Us', category: 'Teachers', enrollmentsNeeded: @state.get('numberOfStudents'), ['Mixpanel']
@openModalView(new TeachersContactModal({ enrollmentsNeeded: @state.get('numberOfStudents') }))
onInputStudentsInput: ->
input = @$('#students-input').val()
if input isnt "" and (parseFloat(input) isnt parseInt(input) or _.isNaN parseInt(input))
@ -106,6 +109,7 @@ module.exports = class EnrollmentsView extends RootView
numberOfStudentsIsValid: -> 0 < @get('numberOfStudents') < 100000
onClickEnrollStudentsButton: ->
window.tracker?.trackEvent 'Classes Licenses Enroll Students', category: 'Teachers', ['Mixpanel']
modal = new ActivateLicensesModal({ selectedUsers: @notEnrolledUsers, users: @members })
@openModalView(modal)
modal.once 'hidden', =>

View file

@ -2,4 +2,7 @@ RootView = require 'views/core/RootView'
module.exports = class RestrictedToStudentsView extends RootView
id: 'restricted-to-students-view'
template: require 'templates/courses/restricted-to-students-view'
template: require 'templates/courses/restricted-to-students-view'
initialize: ->
window.tracker?.trackEvent 'Restricted To Students Loaded', category: 'Students', ['Mixpanel']

View file

@ -120,6 +120,7 @@ module.exports = class TeacherClassView extends RootView
@supermodel.trackRequest @levels.fetchForClassroom(classroomID, {data: {project: 'original,concepts'}})
@attachMediatorEvents()
window.tracker?.trackEvent 'Teachers Class Loaded', category: 'Teachers', classroomID: @classroom.id, ['Mixpanel']
attachMediatorEvents: () ->
# Model/Collection events
@ -211,31 +212,35 @@ module.exports = class TeacherClassView extends RootView
window.location.hash = hash
onClickCopyCodeButton: ->
window.tracker?.trackEvent 'Teachers Class Copy Class Code', category: 'Teachers', classroomID: @classroom.id, classCode: @state.get('classCode'), ['Mixpanel']
@$('#join-code-input').val(@state.get('classCode')).select()
@tryCopy()
onClickCopyURLButton: ->
window.tracker?.trackEvent 'Teachers Class Copy Class URL', category: 'Teachers', classroomID: @classroom.id, url: @state.get('joinURL'), ['Mixpanel']
@$('#join-url-input').val(@state.get('joinURL')).select()
@tryCopy()
tryCopy: ->
try
document.execCommand('copy')
application.tracker?.trackEvent 'Classroom copy URL', category: 'Courses', classroomID: @classroom.id, url: @state.joinURL
catch err
message = 'Oops, unable to copy'
noty text: message, layout: 'topCenter', type: 'error', killer: false
onClickUnarchive: ->
window.tracker?.trackEvent 'Teachers Class Unarchive', category: 'Teachers', classroomID: @classroom.id, ['Mixpanel']
@classroom.save { archived: false }
onClickEditClassroom: (e) ->
window.tracker?.trackEvent 'Teachers Class Edit Class Started', category: 'Teachers', classroomID: @classroom.id, ['Mixpanel']
classroom = @classroom
modal = new ClassroomSettingsModal({ classroom: classroom })
@openModalView(modal)
@listenToOnce modal, 'hide', @render
onClickEditStudentLink: (e) ->
window.tracker?.trackEvent 'Teachers Class Students Edit', category: 'Teachers', classroomID: @classroom.id, ['Mixpanel']
user = @students.get($(e.currentTarget).data('student-id'))
modal = new EditStudentModal({ user, @classroom })
@openModalView(modal)
@ -252,9 +257,10 @@ module.exports = class TeacherClassView extends RootView
onStudentRemoved: (e) ->
@students.remove(e.user)
application.tracker?.trackEvent 'Classroom removed student', category: 'Courses', classroomID: @classroom.id, userID: e.user.id
window.tracker?.trackEvent 'Teachers Class Students Removed', category: 'Teachers', classroomID: @classroom.id, userID: e.user.id, ['Mixpanel']
onClickAddStudents: (e) =>
window.tracker?.trackEvent 'Teachers Class Add Students', category: 'Teachers', classroomID: @classroom.id, ['Mixpanel']
modal = new InviteToClassroomModal({ classroom: @classroom })
@openModalView(modal)
@listenToOnce modal, 'hide', @render
@ -294,14 +300,14 @@ module.exports = class TeacherClassView extends RootView
user = @students.get(userID)
selectedUsers = new Users([user])
@enrollStudents(selectedUsers)
window.tracker?.trackEvent $(e.currentTarget).data('event-action'), category: 'Teachers', classroomID: @classroom.id, userID: userID, ['Mixpanel']
onClickBulkEnroll: ->
courseID = @$('.bulk-course-select').val()
courseInstance = @courseInstances.findWhere({ courseID, classroomID: @classroom.id })
userIDs = @getSelectedStudentIDs().toArray()
selectedUsers = new Users(@students.get(userID) for userID in userIDs)
@enrollStudents(selectedUsers)
window.tracker?.trackEvent 'Teachers Class Students Enroll Selected', category: 'Teachers', classroomID: @classroom.id, ['Mixpanel']
enrollStudents: (selectedUsers) ->
modal = new ActivateLicensesModal { @classroom, selectedUsers, users: @students }
@openModalView(modal)
@ -311,10 +317,10 @@ module.exports = class TeacherClassView extends RootView
if user
user.set(newUser.attributes)
null
application.tracker?.trackEvent 'Classroom started enroll students', category: 'Courses'
onClickExportStudentProgress: ->
# TODO: Does not yield .csv download on Safari, and instead opens a new tab with the .csv contents
window.tracker?.trackEvent 'Teachers Class Export CSV', category: 'Teachers', classroomID: @classroom.id, ['Mixpanel']
csvContent = "data:text/csv;charset=utf-8,Username, Email, Playtime, Concepts\n"
for student in @students.models
concepts = []
@ -336,15 +342,14 @@ module.exports = class TeacherClassView extends RootView
encodedUri = encodeURI(csvContent)
window.open(encodedUri)
onClickAssignStudentButton: (e) ->
userID = $(e.currentTarget).data('user-id')
user = @students.get(userID)
members = [userID]
courseID = $(e.currentTarget).data('course-id')
@assignCourse courseID, members
window.tracker?.trackEvent 'Teachers Class Students Assign Selected', category: 'Teachers', classroomID: @classroom.id, courseID: courseID, userID: userID, ['Mixpanel']
onClickBulkAssign: ->
courseID = @$('.bulk-course-select').val()
selectedIDs = @getSelectedStudentIDs()
@ -352,16 +357,13 @@ module.exports = class TeacherClassView extends RootView
user = @students.get(userID)
user.isEnrolled()
).toArray()
assigningToUnenrolled = _.any selectedIDs, (userID) =>
not @students.get(userID).isEnrolled()
assigningToNobody = selectedIDs.length is 0
@state.set errors: { assigningToNobody, assigningToUnenrolled }
@assignCourse courseID, members
window.tracker?.trackEvent 'Teachers Class Students Assign Selected', category: 'Teachers', classroomID: @classroom.id, courseID: courseID, ['Mixpanel']
# TODO: Move this to the model. Use promises/callbacks?
assignCourse: (courseID, members) ->
courseInstance = @courseInstances.findWhere({ courseID, classroomID: @classroom.id })

View file

@ -17,13 +17,16 @@ helper = require 'lib/coursesHelper'
module.exports = class TeacherClassesView extends RootView
id: 'teacher-classes-view'
template: template
events:
'click .edit-classroom': 'onClickEditClassroom'
'click .archive-classroom': 'onClickArchiveClassroom'
'click .unarchive-classroom': 'onClickUnarchiveClassroom'
'click .add-students-btn': 'onClickAddStudentsButton'
'click .create-classroom-btn': 'onClickCreateClassroomButton'
'click .create-teacher-btn': 'onClickCreateTeacherButton'
'click .update-teacher-btn': 'onClickUpdateTeacherButton'
'click .view-class-btn': 'onClickViewClassButton'
getTitle: -> return $.i18n.t('teacher.my_classes')
@ -38,18 +41,19 @@ module.exports = class TeacherClassesView extends RootView
jqxhrs = classroom.sessions.fetchForAllClassroomMembers(classroom)
if jqxhrs.length > 0
@supermodel.trackCollection(classroom.sessions)
window.tracker?.trackEvent 'Teachers Classes Loaded', category: 'Teachers', ['Mixpanel']
@courses = new Courses()
@courses.fetch()
@supermodel.trackCollection(@courses)
@courseInstances = new CourseInstances()
@courseInstances.fetchByOwner(me.id)
@supermodel.trackCollection(@courseInstances)
@progressDotTemplate = require 'templates/teachers/hovers/progress-dot-whole-course'
# Level Sessions loaded after onLoaded to prevent race condition in calculateDots
afterRender: ->
super()
$('.progress-dot').each (i, el) ->
@ -58,52 +62,71 @@ module.exports = class TeacherClassesView extends RootView
html: true
container: dot
})
onLoaded: ->
helper.calculateDots(@classrooms, @courses, @courseInstances)
super()
onClickEditClassroom: (e) ->
classroomID = $(e.target).data('classroom-id')
window.tracker?.trackEvent $(e.target).data('event-action'), category: 'Teachers', classroomID: classroomID, ['Mixpanel']
classroom = @classrooms.get(classroomID)
modal = new ClassroomSettingsModal({ classroom: classroom })
@openModalView(modal)
@listenToOnce modal, 'hide', @render
onClickCreateClassroomButton: (e) ->
window.tracker?.trackEvent 'Teachers Classes Create New Class Started', category: 'Teachers', ['Mixpanel']
classroom = new Classroom({ ownerID: me.id })
modal = new ClassroomSettingsModal({ classroom: classroom })
@openModalView(modal)
@listenToOnce modal.classroom, 'sync', ->
window.tracker?.trackEvent 'Teachers Classes Create New Class Finished', category: 'Teachers', ['Mixpanel']
@classrooms.add(modal.classroom)
@addFreeCourseInstances()
@render()
onClickCreateTeacherButton: (e) ->
window.tracker?.trackEvent $(e.target).data('event-action'), category: 'Teachers', ['Mixpanel']
application.router.navigate("/teachers/signup", { trigger: true })
onClickUpdateTeacherButton: (e) ->
window.tracker?.trackEvent $(e.target).data('event-action'), category: 'Teachers', ['Mixpanel']
application.router.navigate("/teachers/update-account", { trigger: true })
onClickAddStudentsButton: (e) ->
window.tracker?.trackEvent 'Teachers Classes Add Students Started', category: 'Teachers', ['Mixpanel']
classroomID = $(e.currentTarget).data('classroom-id')
classroom = @classrooms.get(classroomID)
modal = new InviteToClassroomModal({ classroom: classroom })
@openModalView(modal)
@listenToOnce modal, 'hide', @render
onClickArchiveClassroom: (e) ->
classroomID = $(e.currentTarget).data('classroom-id')
classroom = @classrooms.get(classroomID)
classroom.set('archived', true)
classroom.save {}, {
success: =>
window.tracker?.trackEvent 'Teachers Classes Archived Class', category: 'Teachers', ['Mixpanel']
@render()
}
onClickUnarchiveClassroom: (e) ->
classroomID = $(e.currentTarget).data('classroom-id')
classroom = @classrooms.get(classroomID)
classroom.set('archived', false)
classroom.save {}, {
success: =>
window.tracker?.trackEvent 'Teachers Classes Unarchived Class', category: 'Teachers', ['Mixpanel']
@render()
}
onClickViewClassButton: (e) ->
classroomID = $(e.target).data('classroom-id')
window.tracker?.trackEvent $(e.target).data('event-action'), category: 'Teachers', classroomID: classroomID, ['Mixpanel']
application.router.navigate("/teachers/classes/#{classroomID}", { trigger: true })
addFreeCourseInstances: ->
# so that when students join the classroom, they can automatically get free courses
# non-free courses are generated when the teacher first adds a student to them

View file

@ -1,4 +1,3 @@
ActivateLicensesModal = require 'views/courses/ActivateLicensesModal'
app = require 'core/application'
CocoCollection = require 'collections/CocoCollection'
CocoModel = require 'models/CocoModel'
@ -6,24 +5,19 @@ Course = require 'models/Course'
Campaigns = require 'collections/Campaigns'
Classroom = require 'models/Classroom'
Classrooms = require 'collections/Classrooms'
InviteToClassroomModal = require 'views/courses/InviteToClassroomModal'
User = require 'models/User'
CourseInstance = require 'models/CourseInstance'
RootView = require 'views/core/RootView'
template = require 'templates/courses/teacher-courses-view'
ClassroomSettingsModal = require 'views/courses/ClassroomSettingsModal'
module.exports = class TeacherCoursesView extends RootView
id: 'teacher-courses-view'
template: template
events:
'click #activate-licenses-btn': 'onClickActivateLicensesButton'
'click .btn-add-students': 'onClickAddStudents'
'click .create-new-class': 'onClickCreateNewClassButton'
'click .edit-classroom-small': 'onClickEditClassroomSmall'
'click .guide-btn': 'onClickGuideButton'
'click .play-level-button': 'onClickPlayLevel'
guideLinks:
{
"560f1a9f22961295f9427742":
@ -48,91 +42,25 @@ module.exports = class TeacherCoursesView extends RootView
@supermodel.trackCollection(@ownedClassrooms)
@courses = new CocoCollection([], { url: "/db/course", model: Course})
@supermodel.loadCollection(@courses, 'courses')
@classrooms = new CocoCollection([], { url: "/db/classroom", model: Classroom })
@classrooms.comparator = '_id'
@listenToOnce @classrooms, 'sync', @onceClassroomsSync
@supermodel.loadCollection(@classrooms, 'classrooms', {data: {ownerID: me.id}})
@campaigns = new Campaigns()
@supermodel.trackRequest @campaigns.fetchByType('course', { data: { project: 'levels,levelsUpdated' } })
@courseInstances = new CocoCollection([], { url: "/db/course_instance", model: CourseInstance })
@courseInstances.comparator = 'courseID'
@courseInstances.sliceWithMembers = -> return @filter (courseInstance) -> _.size(courseInstance.get('members')) and courseInstance.get('classroomID')
@supermodel.loadCollection(@courseInstances, 'course_instances', {data: {ownerID: me.id}})
@members = new CocoCollection([], { model: User })
@listenTo @members, 'sync', @render
@
onceClassroomsSync: ->
for classroom in @classrooms.models
@members.fetch({
remove: false
url: "/db/classroom/#{classroom.id}/members"
})
initialize: (options) ->
window.tracker?.trackEvent 'Classes Guides Loaded', category: 'Teachers', ['Mixpanel']
super(options)
onClickActivateLicensesButton: ->
modal = new ActivateLicensesModal({
users: @members
})
@openModalView(modal)
modal.once 'redeem-users', -> document.location.reload()
application.tracker?.trackEvent 'Courses teachers started enroll students', category: 'Courses'
onClickGuideButton: (e) ->
courseID = $(e.currentTarget).data('course-id')
courseName = $(e.currentTarget).data('course-name')
eventAction = $(e.currentTarget).data('event-action')
window.tracker?.trackEvent eventAction, category: 'Teachers', courseID: courseID, courseName: courseName, ['Mixpanel']
onClickAddStudents: (e) ->
classroomID = $(e.target).data('classroom-id')
classroom = @classrooms.get(classroomID)
unless classroom
console.error 'No classroom ID found.'
return
modal = new InviteToClassroomModal({classroom: classroom})
@openModalView(modal)
application.tracker?.trackEvent 'Classroom started add students', category: 'Courses', classroomID: classroom.id
onClickCreateNewClassButton: ->
return application.router.navigate('/teachers/signup', {trigger: true}) if me.get('anonymous')
modal = new ClassroomSettingsModal({})
@openModalView(modal)
@listenToOnce modal, 'hide', =>
# TODO: how to get new classroom from modal?
@classrooms.add(modal.classroom)
# TODO: will this definitely fire after modal saves new classroom?
@listenToOnce modal.classroom, 'sync', ->
@addFreeCourseInstances()
@render()
onClickEditClassroomSmall: (e) ->
classroomID = $(e.target).data('classroom-id')
classroom = @classrooms.get(classroomID)
modal = new ClassroomSettingsModal({classroom: classroom})
@openModalView(modal)
@listenToOnce modal, 'hide', @render
onClickPlayLevel: (e) ->
form = $(e.currentTarget).closest('.play-level-form')
levelSlug = form.find('.level-select').val()
courseID = form.data('course-id')
language = form.find('.language-select').val()
window.tracker?.trackEvent 'Classes Guides Play Level', category: 'Teachers', courseID: courseID, language: language, levelSlug: levelSlug, ['Mixpanel']
url = "/play/level/#{levelSlug}?course=#{courseID}&codeLanguage=#{language}"
application.router.navigate(url, { trigger: true })
onLoaded: ->
super()
@addFreeCourseInstances()
addFreeCourseInstances: ->
# so that when students join the classroom, they can automatically get free courses
# non-free courses are generated when the teacher first adds a student to them
for classroom in @classrooms.models
for course in @courses.models
continue if not course.get('free')
courseInstance = @courseInstances.findWhere({classroomID: classroom.id, courseID: course.id})
if not courseInstance
courseInstance = new CourseInstance({
classroomID: classroom.id
courseID: course.id
})
# TODO: figure out a better way to get around triggering validation errors for properties
# that the server will end up filling in, like an empty members array, ownerID
courseInstance.save(null, {validate: false})
@courseInstances.add(courseInstance)
@listenToOnce courseInstance, 'sync', @addFreeCourseInstances
return

View file

@ -34,7 +34,8 @@ module.exports = class ControlBarView extends CocoView
@worldName = options.worldName
@session = options.session
@level = options.level
@levelID = @level.get('slug') or @level.id
@levelSlug = @level.get('slug')
@levelID = @levelSlug or @level.id
@spectateGame = options.spectateGame ? false
@observing = options.session.get('creator') isnt me.id
super options
@ -121,6 +122,9 @@ module.exports = class ControlBarView extends CocoView
@setupManager.open()
onClickHome: (e) ->
if @level.get('type', true) in ['course']
category = if me.isTeacher() then 'Teachers' else 'Students'
window.tracker?.trackEvent 'Play Level Back To Levels', category: category, levelSlug: @levelSlug, ['Mixpanel']
e.preventDefault()
e.stopImmediatePropagation()
Backbone.Mediator.publish 'router:navigate', route: @homeLink, viewClass: @homeViewClass, viewArgs: @homeViewArgs

View file

@ -56,6 +56,7 @@ module.exports = class CourseVictoryModal extends ModalView
@levelSessions = @supermodel.loadCollection(@levelSessions, 'sessions', {
data: { project: 'state.complete level.original playtime changed' }
}).model
window.tracker?.trackEvent 'Play Level Victory Modal Loaded', category: 'Students', levelSlug: @level.get('slug'), ['Mixpanel']
onResourceLoadFailed: (e) ->
if e.resource.jqxhr is @nextLevelRequest
@ -158,6 +159,7 @@ module.exports = class CourseVictoryModal extends ModalView
@showView(@views[index+1])
onNextLevel: ->
window.tracker?.trackEvent 'Play Level Victory Modal Next Level', category: 'Students', levelSlug: @level.get('slug'), nextLevelSlug: @nextLevel.get('slug'), ['Mixpanel']
if me.isSessionless()
link = "/play/level/#{@nextLevel.get('slug')}?course=#{@courseID}&codeLanguage=#{utils.getQueryVariable('codeLanguage', 'python')}"
else
@ -165,6 +167,7 @@ module.exports = class CourseVictoryModal extends ModalView
application.router.navigate(link, {trigger: true})
onDone: ->
window.tracker?.trackEvent 'Play Level Victory Modal Done', category: 'Students', levelSlug: @level.get('slug'), ['Mixpanel']
if me.isSessionless()
link = "/teachers/courses"
else

View file

@ -33,6 +33,7 @@ module.exports = class ConvertToTeacherAccountView extends RootView
@trialRequests = new TrialRequests()
@trialRequests.fetchOwn()
@supermodel.trackCollection(@trialRequests)
window.tracker?.trackEvent 'Teachers Convert Account Loaded', category: 'Teachers', ['Mixpanel']
onLeaveMessage: ->
if @formChanged
@ -87,6 +88,8 @@ module.exports = class ConvertToTeacherAccountView extends RootView
@onChangeForm()
onChangeForm: ->
unless @formChanged
window.tracker?.trackEvent 'Teachers Convert Account Form Started', category: 'Teachers', ['Mixpanel']
@formChanged = true
onSubmitForm: (e) ->
@ -141,6 +144,7 @@ module.exports = class ConvertToTeacherAccountView extends RootView
errors.showNotyNetworkError(arguments...)
onTrialRequestSubmit: ->
window.tracker?.trackEvent 'Teachers Convert Account Submitted', category: 'Teachers', ['Mixpanel']
@formChanged = false
me.setRole @trialRequest.get('properties').role.toLowerCase(), true
application.router.navigate('/teachers/classes', {trigger: true})

View file

@ -31,6 +31,7 @@ module.exports = class CreateTeacherAccountView extends RootView
@trialRequests = new TrialRequests()
@trialRequests.fetchOwn()
@supermodel.trackCollection(@trialRequests)
window.tracker?.trackEvent 'Teachers Create Account Loaded', category: 'Teachers', ['Mixpanel']
onLeaveMessage: ->
if @formChanged
@ -39,8 +40,6 @@ module.exports = class CreateTeacherAccountView extends RootView
onLoaded: ->
if @trialRequests.size()
@trialRequest = @trialRequests.first()
if @trialRequest and @trialRequest.get('status') isnt 'submitted' and @trialRequest.get('status') isnt 'approved'
window.tracker?.trackEvent 'View Trial Request', category: 'Teachers', label: 'View Trial Request', ['Mixpanel']
super()
invalidateNCES: ->
@ -90,6 +89,8 @@ module.exports = class CreateTeacherAccountView extends RootView
@openModalView(modal)
onChangeForm: ->
unless @formChanged
window.tracker?.trackEvent 'Teachers Create Account Form Started', category: 'Teachers', ['Mixpanel']
@formChanged = true
onSubmitForm: (e) ->
@ -158,6 +159,7 @@ module.exports = class CreateTeacherAccountView extends RootView
@openModalView(modal)
onTrialRequestSubmit: ->
window.tracker?.trackEvent 'Teachers Create Account Submitted', category: 'Teachers', ['Mixpanel']
@formChanged = false
attrs = _.pick(forms.formToObject(@$('form')), 'name', 'email', 'role')
attrs.role = attrs.role.toLowerCase()

View file

@ -34,6 +34,7 @@ module.exports = class RequestQuoteView extends RootView
@trialRequests.fetchOwn()
@supermodel.trackCollection(@trialRequests)
@formChanged = false
window.tracker?.trackEvent 'Teachers Request Demo Loaded', category: 'Teachers', ['Mixpanel']
onLeaveMessage: ->
if @formChanged
@ -42,8 +43,6 @@ module.exports = class RequestQuoteView extends RootView
onLoaded: ->
if @trialRequests.size()
@trialRequest = @trialRequests.first()
if @trialRequest and @trialRequest.get('status') isnt 'submitted' and @trialRequest.get('status') isnt 'approved'
window.tracker?.trackEvent 'View Trial Request', category: 'Teachers', label: 'View Trial Request', ['Mixpanel']
super()
invalidateNCES: ->
@ -89,6 +88,8 @@ module.exports = class RequestQuoteView extends RootView
@onChangeRequestForm()
onChangeRequestForm: ->
unless @formChanged
window.tracker?.trackEvent 'Teachers Request Demo Form Started', category: 'Teachers', ['Mixpanel']
@formChanged = true
onSubmitRequestForm: (e) ->
@ -161,6 +162,7 @@ module.exports = class RequestQuoteView extends RootView
@openModalView(modal)
onTrialRequestSubmit: ->
window.tracker?.trackEvent 'Teachers Request Demo Form Submitted', category: 'Teachers', ['Mixpanel']
@formChanged = false
me.setRole @trialRequest.get('properties').role.toLowerCase(), true
defaultName = [@trialRequest.get('firstName'), @trialRequest.get('lastName')].join(' ')
@ -168,7 +170,6 @@ module.exports = class RequestQuoteView extends RootView
@$('#request-form, #form-submit-success').toggleClass('hide')
@scrollToTop(0)
$('#flying-focus').css({top: 0, left: 0}) # Hack copied from Router.coffee#187. Ideally we'd swap out the view and have view-swapping logic handle this
window.tracker?.trackEvent 'Submit Trial Request', category: 'Teachers', label: 'Trial Request', ['Mixpanel']
onClickGPlusSignupButton: ->
btn = @$('#gplus-signup-btn')
@ -190,6 +191,7 @@ module.exports = class RequestQuoteView extends RootView
url: "/db/user?gplusID=#{gplusAttrs.gplusID}&gplusAccessToken=#{application.gplusHandler.token()}"
type: 'PUT'
success: ->
window.tracker?.trackEvent 'Teachers Request Demo Create Account Google', category: 'Teachers', ['Mixpanel']
application.router.navigate(SIGNUP_REDIRECT)
window.location.reload()
error: errors.showNotyNetworkError
@ -218,6 +220,7 @@ module.exports = class RequestQuoteView extends RootView
url: "/db/user?facebookID=#{facebookAttrs.facebookID}&facebookAccessToken=#{application.facebookHandler.token()}"
type: 'PUT'
success: ->
window.tracker?.trackEvent 'Teachers Request Demo Create Account Facebook', category: 'Teachers', ['Mixpanel']
application.router.navigate(SIGNUP_REDIRECT)
window.location.reload()
error: errors.showNotyNetworkError
@ -250,13 +253,12 @@ module.exports = class RequestQuoteView extends RootView
})
me.save(null, {
success: ->
window.tracker?.trackEvent 'Teachers Request Demo Create Account', category: 'Teachers', ['Mixpanel']
application.router.navigate(SIGNUP_REDIRECT)
window.location.reload()
error: errors.showNotyNetworkError
})
requestFormSchemaAnonymous = {
type: 'object'
required: [

View file

@ -2,4 +2,7 @@ RootView = require 'views/core/RootView'
module.exports = class RestrictedToTeachersView extends RootView
id: 'restricted-to-teachers-view'
template: require 'templates/teachers/restricted-to-teachers-view'
template: require 'templates/teachers/restricted-to-teachers-view'
initialize: ->
window.tracker?.trackEvent 'Restricted To Teachers Loaded', category: 'Students', ['Mixpanel']