Merge branch 'master' into production

This commit is contained in:
Matt Lott 2015-12-02 17:00:45 -08:00
commit 1d51b063c4
13 changed files with 137 additions and 65 deletions

Binary file not shown.

View file

@ -37,7 +37,8 @@
margin-bottom: 20px
img.media-object
width: 300px
width: 100%
margin-bottom: 5px
.edit-classroom-small
cursor: pointer

View file

@ -1,8 +1,15 @@
#teachers-view
font-size: 16px
.bigger-text
font-size: 18px
.faq-question
font-size: 18px
font-weight: bold
margin: 20px 0px 10px 0px
.uppercase
text-transform: uppercase
@ -16,3 +23,6 @@
.text-center
text-align: center
.title
font-size: 20px

View file

@ -53,7 +53,7 @@ block content
.progress
.progress-bar(style='width:'+stats.levels.pctDone)
else if paidFor
button.enable-btn.btn.btn-info.btn-sm(data-user-id=user.id, data-course-instance-id=courseInstance.id) Enable
button.enable-btn.btn.btn-info.btn-sm(data-user-id=user.id, data-course-instance-cid=courseInstance.cid) Enable
if !paidFor
.text-center

View file

@ -244,7 +244,7 @@ mixin progress-members
mixin progress-members-individual(memberID)
- var name = memberUserMap[memberID] ? memberUserMap[memberID].get('name') : 'Anoner'
a(href="/user/#{memberID}")= name || 'Anoner'
strong= name || 'Anoner'
if memberStats && memberStats[memberID]
div
span #{memberStats[memberID].totalLevelsCompleted}

View file

@ -8,6 +8,33 @@ block content
else
.welcome Welcome, #{me.get('name')}!
.container-fluid
.row
.col-md-2
.col-md-8
.well
.text-center
strong.uppercase Getting Started with Courses
br
.text-center
a.btn.btn-info(href='/docs/HourofCodeGettingStartedGuide.pdf') Download Getting Started Guide [PDF]
br
ol
li Create a new class by clicking the green "Create New Class" button below.
li Once you've created a class, click the blue "Add Students" button.
li You'll see student's progress below as they sign up and join your class.
br
.text-center
strong Additional Resources
ul
li
span.spr Complete our
a.spr(href='/teachers/freetrial') Teacher Survey
span to get 15 more hours of content for FREE for 2 months.
li
span.spr Visit our
a.spr(href='http://discourse.codecombat.com/c/teachers') Teacher Forums
span to connect to fellow educators who are using CodeCombat.
.section-header Your Classes

View file

@ -1,15 +1,25 @@
extends /templates/base
block content
p.bigger-text
strong Teachers
span - share CodeCombat's Hour of Code with your class!
.container-fluid
.row
.col-md-6
.bigger-text
.pull-left
a(href="/") <- to main CodeCombat website
.col-md-6
.pull-right
a(href="/hoc") Students, click here!
.row
.col-md-12
br
.text-center
.title
strong.spr Teachers
span - share CodeCombat's Hour of Code with your class!
.row
.col-md-4
.col-md-4
p
div In just one hour, students will learn:
ul
li basic Python syntex
@ -17,19 +27,7 @@ block content
li strings
li while loops
li variables
.col-md-5
.well
strong.uppercase These easy steps to get started:
ol
if me.isAnonymous()
li
a.spr.link-register Register
span for a free teacher account to manage classes and monitor student progress
else
li Register (you've already done this)
li
a.spr(href='/courses/teachers') Create a class
span and invite students via email, unique passcode, or URL.
br
if me.isAnonymous()
.text-center
@ -37,38 +35,24 @@ block content
.text-center
button.btn.btn-lg.btn-primary.uppercase.btn-login-account Log into Teacher Account
h2 Free Trial for Teachers!
p
strong.spr Hour of Code Special!
span Complete the survey by December 31st and enroll all your students in the paid courses for 2 months.
p(data-i18n="teachers.teacher_subs_0")
p
span.spr(data-i18n="teachers.teacher_subs_1")
a(href='/teachers/freetrial', data-i18n="teachers.teacher_subs_2")
span.spl(data-i18n="teachers.teacher_subs_3")
br
br
h2.text-center Hour of Code FAQ
h2 FAQ
.container-fluid
.row
.col-md-2
.col-md-8
.faq-question(data-i18n="teachers.who_for_title")
p(data-i18n="teachers.who_for_1")
p(data-i18n="teachers.who_for_2")
h3(data-i18n="teachers.who_for_title")
p(data-i18n="teachers.who_for_1")
p(data-i18n="teachers.who_for_2")
.faq-question Does it cost anything to play Hour of Code?
p No! The first hour of CodeCombat is completely free.
p Teachers, please see the free trial information above for further details.
h3 Does it cost anything to play Hour of Code?
p No! The first hour of CodeCombat is completely free.
p Teachers, please see the free trial information above for further details.
//- h3 How much are the paid courses?
//- p
//- span.spr(data-i18n="teachers.free_3")
//- a(href='/courses', data-i18n="teachers.free_4")
//- span(data-i18n="teachers.free_5")
//- p(data-i18n="teachers.free_6")
//- p
//- span.spr For more details, please email
//- a(href='mailto:team@codecombat.com') team@codecombat.com
h3(data-i18n="teachers.more_info_title")
p
span.spr(data-i18n="teachers.more_info_1")
a(href='http://discourse.codecombat.com/c/teachers', data-i18n="teachers.more_info_2")
span.spl(data-i18n="teachers.more_info_3")
.faq-question(data-i18n="teachers.more_info_title")
p
span.spr(data-i18n="teachers.more_info_1")
a(href='http://discourse.codecombat.com/c/teachers', data-i18n="teachers.more_info_2")
span.spl(data-i18n="teachers.more_info_3")

View file

@ -11,6 +11,12 @@ module.exports = class TeachersView extends RootView
'click .btn-login-account': 'onClickLogin'
'click .link-register': 'onClickSignup'
constructor: ->
super()
unless me.isAnonymous()
_.defer ->
application.router.navigate "/courses/teachers", trigger: true
onClickLogin: (e) ->
@openModalView new AuthModal(mode: 'login') if me.get('anonymous')

View file

@ -50,10 +50,22 @@ module.exports = class ClassroomView extends RootView
@supermodel.loadCollection(sessions, 'sessions')
courseInstance.sessions = sessions
sessions.courseInstance = courseInstance
courseInstance.sessionsByUser = {}
@listenToOnce sessions, 'sync', (sessions) ->
@sessions.add(sessions.slice())
sessions.courseInstance.sessionsByUser = sessions.groupBy('creator')
# generate course instance JIT, in the meantime have models w/out equivalents in the db
for course in @courses.models
query = {courseID: course.id, classroomID: @classroom.id}
courseInstance = @courseInstances.findWhere(query)
if not courseInstance
courseInstance = new CourseInstance(query)
@courseInstances.add(courseInstance)
courseInstance.sessions = new CocoCollection([], {model: LevelSession})
sessions.courseInstance = courseInstance
courseInstance.sessionsByUser = {}
onLoaded: ->
userSessions = @sessions.groupBy('creator')
for user in @users.models
@ -105,11 +117,20 @@ module.exports = class ClassroomView extends RootView
@openModalView(modal)
onClickEnableButton: (e) ->
courseInstance = @courseInstances.get($(e.target).data('course-instance-id'))
courseInstance = @courseInstances.get($(e.target).data('course-instance-cid'))
userID = $(e.target).data('user-id')
courseInstance.addMember(userID)
$(e.target).attr('disabled', true)
@listenToOnce courseInstance, 'sync', @render
onCourseInstanceCreated = =>
courseInstance.addMember(userID)
@listenToOnce courseInstance, 'sync', @render
if courseInstance.isNew()
# adding the first student to this course, so generate the course instance for it
courseInstance.save(null, {validate: false})
courseInstance.once 'sync', onCourseInstanceCreated
else
onCourseInstanceCreated()
onClickRemoveStudentLink: (e) ->
user = @users.get($(e.target).closest('a').data('user-id'))

View file

@ -7,7 +7,7 @@ module.exports = class InviteToClassroomModal extends ModalView
events:
'click #send-invites-btn': 'onClickSendInvitesButton'
'click #copy-url-btn': 'onClickCopyURLButton'
'click #copy-url-btn, #join-url-input': 'copyURL'
initialize: (options) ->
@classroom = options.classroom
@ -34,7 +34,7 @@ module.exports = class InviteToClassroomModal extends ModalView
@$('#invite-emails-success-alert').removeClass('hide')
})
onClickCopyURLButton: ->
copyURL: ->
@$('#join-url-input').val(@joinURL).select()
try
document.execCommand('copy')

View file

@ -26,7 +26,7 @@ module.exports = class PurchaseCoursesView extends RootView
getPrice: -> @pricePerStudent * @numberOfStudents
onInputStudentsInput: ->
@numberOfStudents = parseInt(@$('#students-input').val()) or 0
@numberOfStudents = Math.max(parseInt(@$('#students-input').val()) or 0, 0)
@updatePrice()
updatePrice: ->
@ -54,7 +54,6 @@ module.exports = class PurchaseCoursesView extends RootView
onStripeReceivedToken: (e) ->
@state = 'purchasing'
@render?()
console.log 'e', e
data =
maxRedeemers: @numberOfStudents

View file

@ -60,7 +60,9 @@ module.exports = class TeacherCoursesView extends RootView
# 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', @render
@listenToOnce modal.classroom, 'sync', ->
@addFreeCourseInstances()
@render()
onClickEditClassroomSmall: (e) ->
classroomID = $(e.target).data('classroom-id')
@ -68,3 +70,26 @@ module.exports = class TeacherCoursesView extends RootView
modal = new ClassroomSettingsModal({classroom: classroom})
@openModalView(modal)
@listenToOnce modal, 'hide', @render
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

@ -106,8 +106,7 @@ ClassroomHandler = class ClassroomHandler extends Handler
address: email
email_data:
class_name: classroom.get('name')
# TODO: join_link
join_link: "https://codecombat.com/courses/students?_cc=" + (classroom.get('codeCamel') or classroom.get('code'))
join_link: "https://codecombat.com/courses?_cc=" + (classroom.get('codeCamel') or classroom.get('code'))
sendwithus.api.send context, _.noop
return @sendSuccess(res, {})