mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-03-14 07:00:01 -04:00
Add framework for new courses pages
This commit is contained in:
parent
aafdce6fbe
commit
66432990f4
16 changed files with 240 additions and 555 deletions
|
@ -66,8 +66,8 @@ module.exports = class CocoRouter extends Backbone.Router
|
|||
'courses/mock1/enroll/:courseID': go('courses/mock1/CourseEnrollView')
|
||||
'courses/mock1/:courseID': go('courses/mock1/CourseDetailsView')
|
||||
'courses': go('courses/CoursesView')
|
||||
'courses/students': go('courses/CoursesView')
|
||||
'courses/teachers': go('courses/CoursesView')
|
||||
'courses/students': go('courses/StudentCoursesView')
|
||||
'courses/teachers': go('courses/TeacherCoursesView')
|
||||
'courses/enroll(/:courseID)': go('courses/CourseEnrollView')
|
||||
'courses/:courseID(/:courseInstanceID)': go('courses/CourseDetailsView')
|
||||
|
||||
|
@ -97,7 +97,7 @@ module.exports = class CocoRouter extends Backbone.Router
|
|||
|
||||
'github/*path': 'routeToServer'
|
||||
|
||||
'hoc': go('courses/CoursesView')
|
||||
'hoc': go('courses/HourOfCodeView')
|
||||
|
||||
'i18n': go('i18n/I18NHomeView')
|
||||
'i18n/thang/:handle': go('i18n/I18NEditThangTypeView')
|
||||
|
|
7
app/models/Classroom.coffee
Normal file
7
app/models/Classroom.coffee
Normal file
|
@ -0,0 +1,7 @@
|
|||
CocoModel = require './CocoModel'
|
||||
schema = require 'schemas/models/classroom.schema'
|
||||
|
||||
module.exports = class Classroom extends CocoModel
|
||||
@className: 'Classroom'
|
||||
@schema: schema
|
||||
urlRoot: '/db/classroom'
|
3
app/styles/courses/courses-view.sass
Normal file
3
app/styles/courses/courses-view.sass
Normal file
|
@ -0,0 +1,3 @@
|
|||
#courses-view
|
||||
.row
|
||||
margin-top: 40px
|
|
@ -1,85 +0,0 @@
|
|||
#courses-view
|
||||
|
||||
.logged_out
|
||||
font-size: 24px
|
||||
|
||||
.signup-button
|
||||
background: red
|
||||
color: white
|
||||
font-size: 18px
|
||||
font-variant: small-caps
|
||||
line-height: 27px
|
||||
text-transform: uppercase
|
||||
margin-right: 20px
|
||||
|
||||
.login-button
|
||||
background: white
|
||||
color: black
|
||||
font-size: 18px
|
||||
font-variant: small-caps
|
||||
line-height: 27px
|
||||
text-transform: uppercase
|
||||
|
||||
.center
|
||||
text-align: center
|
||||
|
||||
.code-input
|
||||
width: 100%
|
||||
|
||||
.course-image
|
||||
width: 100%
|
||||
|
||||
.course-panel
|
||||
margin: 20px
|
||||
|
||||
.faq-blurb
|
||||
font-size: 14px
|
||||
|
||||
.continue-dialog .modal-dialog
|
||||
background-color: white
|
||||
max-width: 400px
|
||||
|
||||
.instruction-label
|
||||
font-size: 14pt
|
||||
.or
|
||||
margin-bottom: 20px
|
||||
font-size: 14pt
|
||||
|
||||
.center
|
||||
text-align: center
|
||||
|
||||
.concepts-container
|
||||
width: 200px
|
||||
|
||||
.contact-container
|
||||
margin-top: 20px
|
||||
text-align: center
|
||||
|
||||
.info-container
|
||||
margin: 0% 10%
|
||||
font-size: 18px
|
||||
|
||||
.monitoring-img-container
|
||||
margin-top: 10px
|
||||
|
||||
.praise-caption
|
||||
font-size: 14px
|
||||
|
||||
.praise-quote
|
||||
font-size: 20px
|
||||
font-style: italic
|
||||
|
||||
.progress-container
|
||||
font-size: 20px
|
||||
|
||||
.img-quote
|
||||
height: 160px
|
||||
|
||||
.popover
|
||||
z-index: 1050
|
||||
min-width: 400px
|
||||
|
||||
h3
|
||||
background: transparent
|
||||
border: 0
|
||||
font-size: 30px
|
1
app/styles/courses/hour-of-code-view.sass
Normal file
1
app/styles/courses/hour-of-code-view.sass
Normal file
|
@ -0,0 +1 @@
|
|||
//#hour-of-code-view
|
1
app/styles/courses/student-courses-view.sass
Normal file
1
app/styles/courses/student-courses-view.sass
Normal file
|
@ -0,0 +1 @@
|
|||
//#student-courses-view
|
1
app/styles/courses/teacher-courses-view.sass
Normal file
1
app/styles/courses/teacher-courses-view.sass
Normal file
|
@ -0,0 +1 @@
|
|||
//#teacher-courses-view
|
12
app/templates/courses/courses-view.jade
Normal file
12
app/templates/courses/courses-view.jade
Normal file
|
@ -0,0 +1,12 @@
|
|||
extends /templates/base
|
||||
|
||||
block content
|
||||
|
||||
h1.text-center Welcome to CodeCombat Courses
|
||||
|
||||
.row
|
||||
.col-sm-6.text-center
|
||||
a(href="/courses/students").btn.btn-default Students Click Here
|
||||
|
||||
.col-sm-6.text-center
|
||||
a(href="/courses/teachers").btn.btn-default Teachers Click Here
|
|
@ -1,236 +0,0 @@
|
|||
extends /templates/base
|
||||
|
||||
block content
|
||||
|
||||
div(style='border-bottom: 1px solid black')
|
||||
span *UNDER CONSTRUCTION, please send feedback to
|
||||
a.spl(href='mailto:team@codecombat.com') team@codecombat.com
|
||||
|
||||
br
|
||||
|
||||
.hidden-md.hidden-lg
|
||||
.alert.alert-danger Courses not supported on mobile devices.
|
||||
|
||||
.hidden-xs.hidden-sm
|
||||
if state === 'enrolling'
|
||||
.alert.alert-info Enrolling in course..
|
||||
else if state === 'ppc_logged_out'
|
||||
.alert.alert-danger.logged_out Create account or log in to join this course.
|
||||
button.btn.btn-sm.btn-primary.header-font.signup-button(data-i18n="login.sign_up")
|
||||
button.btn.btn-sm.btn-default.header-font.login-button(data-i18n="login.log_in")
|
||||
else
|
||||
if state === 'unknown_error'
|
||||
.alert.alert-danger.alert-dismissible= stateMessage
|
||||
|
||||
if hocLandingPage
|
||||
+hoc-landing
|
||||
else
|
||||
if view.courseInstances.size()
|
||||
+course-instance-list
|
||||
|
||||
if studentMode
|
||||
+student-main
|
||||
else
|
||||
if hocMode
|
||||
+teacher-hoc
|
||||
else
|
||||
+teacher-main
|
||||
.container-fluid
|
||||
- var i = 0
|
||||
while i < courses.length
|
||||
.row
|
||||
+course-block(courses[i], instances)
|
||||
- i++
|
||||
if i < courses.length
|
||||
+course-block(courses[i], instances)
|
||||
- i++
|
||||
|
||||
|
||||
mixin hoc-landing
|
||||
h1.center Welcome to CodeCombat's Hour of Code!
|
||||
br
|
||||
.container-fluid
|
||||
.row
|
||||
.col-md-6.center
|
||||
button.btn.btn-lg.btn-success.btn-student(data-i18n="courses.students_click")
|
||||
.col-md-6.center
|
||||
button.btn.btn-lg.btn-default.btn-teacher(data-i18n="courses.teachers_click")
|
||||
|
||||
mixin course-instance-list
|
||||
h1.center Courses You Are In
|
||||
.row
|
||||
.col-md-10.col-md-offset-1
|
||||
.list-group
|
||||
for courseInstance in view.courseInstances.models
|
||||
- var course = view.courses.get(courseInstance.get('courseID'));
|
||||
.list-group-item
|
||||
.list-group-item-heading
|
||||
h3
|
||||
a(href="/courses/#{course.id}/#{courseInstance.id}")
|
||||
span.spr #{courseInstance.get('name')}
|
||||
small (#{course.get('name')})
|
||||
p= courseInstance.get('description')
|
||||
|
||||
mixin student-main
|
||||
button.btn.btn-warning.btn-teacher(data-i18n="courses.teachers_click")
|
||||
h1.center(data-i18n="courses.courses_on_coco")
|
||||
|
||||
mixin teacher-hoc
|
||||
button.btn.btn-warning.btn-student(data-i18n="courses.students_click")
|
||||
h1.center Welcome to CodeCombat's Hour of Code!
|
||||
p
|
||||
strong How to use CodeCombat with your students:
|
||||
ol
|
||||
li Click the green 'Get FREE course' button below
|
||||
li Follow the enrollment instructions
|
||||
li Add students via the 'Add Students' tab
|
||||
p
|
||||
span.spr If you have any problems, please email
|
||||
a(href='mailto:team@codecombat.com') team@codecombat.com
|
||||
br
|
||||
|
||||
mixin teacher-main
|
||||
button.btn.btn-warning.btn-student(data-i18n="courses.students_click")
|
||||
h1.center(data-i18n="courses.courses_on_coco")
|
||||
.info-container
|
||||
p(data-i18n="courses.designed_to")
|
||||
.container-fluid
|
||||
.row
|
||||
.col-md-6
|
||||
ul
|
||||
li(data-i18n="courses.more_in_less")
|
||||
li(data-i18n="courses.no_experience")
|
||||
li(data-i18n="courses.easy_monitor")
|
||||
|
||||
p(data-i18n="courses.purchase_for_class")
|
||||
p.faq-blurb
|
||||
span.spr(data-i18n="courses.see_the")
|
||||
a.courses-faq(data-i18n="courses.faq")
|
||||
span.spl(data-i18n="courses.more_info")
|
||||
.col-md-6
|
||||
img.img-quote(src="/images/pages/courses/coco_complab.png")
|
||||
p
|
||||
.well.well-sm
|
||||
div.praise-quote "#{praise.quote}"
|
||||
div.praise-caption - #{praise.source}
|
||||
|
||||
//- h1.center(data-i18n="courses.free_trial")
|
||||
//- .info-container
|
||||
//- p
|
||||
//- span.spr(data-i18n="teachers.teacher_subs_1")
|
||||
//- a(href='/teachers/freetrial', data-i18n="teachers.teacher_subs_2")
|
||||
//- span.spl(data-i18n="courses.get_access")
|
||||
|
||||
h2.center(data-i18n="courses.choose_course")
|
||||
|
||||
mixin student-dialog(course)
|
||||
.modal.continue-dialog(id="continueModal#{course.id}")
|
||||
.modal-dialog
|
||||
.modal-header
|
||||
button.close(data-dismiss='modal')
|
||||
span ×
|
||||
h3.modal-title= course.get('name')
|
||||
.modal-body
|
||||
.container-fluid
|
||||
.row.button-row
|
||||
.col-md-12
|
||||
.well.well-sm
|
||||
p
|
||||
div.instruction-label(data-i18n="courses.enter_code")
|
||||
.container-fluid
|
||||
.row.student-dialog-state-row
|
||||
.col-md-12
|
||||
if view.state === 'enrolling-by-modal'
|
||||
.progress.progress-striped.active
|
||||
.progress-bar(style="width: 100%")
|
||||
else if view.state === 'unknown_error'
|
||||
.alert.alert-danger= view.stateMessage
|
||||
.row
|
||||
.col-md-8
|
||||
input.code-input(type='text', data-course-id="#{course.id}", data-i18n="[placeholder]courses.enter_code1", placeholder="Enter unlock code")
|
||||
.col-md-4
|
||||
button.btn.btn-success.btn-enroll(data-course-id="#{course.id}", data-i18n="courses.enroll")
|
||||
if hocMode && course.get('pricePerSeat') === 0 || me.isAdmin()
|
||||
.row.button-row.center.row-pick-class
|
||||
.col-md-12
|
||||
br
|
||||
div.or(data-i18n="courses.or")
|
||||
.row.button-row.center
|
||||
.col-md-12
|
||||
button.btn.btn-success.btn-lg.btn-hoc-student-continue(data-course-id="#{course.id}") Continue by yourself
|
||||
|
||||
mixin teacher-dialog(course)
|
||||
.modal.continue-dialog(id="continueModal#{course.id}")
|
||||
.modal-dialog
|
||||
.modal-header
|
||||
button.close(data-dismiss='modal')
|
||||
span ×
|
||||
h3.modal-title= course.get('name')
|
||||
.modal-body
|
||||
.container-fluid
|
||||
if enrolledCourses[course.id]
|
||||
.row.button-row.row-pick-class
|
||||
.col-md-12
|
||||
.well.well-sm
|
||||
p
|
||||
div.instruction-label(data-i18n="courses.pick_from_classes")
|
||||
.container-fluid
|
||||
.row
|
||||
.col-md-8
|
||||
select.form-control.select-session(data-course-id="#{course.id}")
|
||||
each inst in instances
|
||||
if inst.get('courseID') == course.id
|
||||
if inst.get('name')
|
||||
option(value="#{inst.id}")= inst.get('name')
|
||||
else
|
||||
option(value="#{inst.id}", data-i18n="courses.unnamed")
|
||||
.col-md-4
|
||||
button.btn.btn-success.btn-enter(data-course-id="#{course.id}", data-i18n="courses.enter")
|
||||
.row.button-row.center.row-pick-class
|
||||
.col-md-12
|
||||
div.or(data-i18n="courses.or")
|
||||
.row.button-row.center
|
||||
.col-md-12
|
||||
if course.get('pricePerSeat') === 0 || me.isAdmin()
|
||||
button.btn.btn-success.btn-lg.btn-buy(data-course-id="#{course.id}") Start new class
|
||||
else
|
||||
button.btn.btn-success.btn-lg.btn-buy(data-course-id="#{course.id}", data-i18n="courses.buy_course1")
|
||||
|
||||
mixin course-block(course)
|
||||
if studentMode
|
||||
+student-dialog(course)
|
||||
else
|
||||
+teacher-dialog(course)
|
||||
.col-md-6
|
||||
.well.panel.course-panel(class=enrolledCourses[course.id] ? 'panel-success' : 'panel-info')
|
||||
.panel-heading
|
||||
.panel-title
|
||||
span.spr #{course.get('name')}
|
||||
strong #{enrolledCourses[course.id] ? '[ enrolled ]' : ''}
|
||||
.panel-body
|
||||
.container-fluid
|
||||
.row
|
||||
.col-md-12
|
||||
p
|
||||
img.course-image(src="#{course.get('screenshot')}")
|
||||
.row.button-row
|
||||
.col-md-6
|
||||
strong(data-i18n="courses.topics")
|
||||
ul
|
||||
each concept in course.get('concepts')
|
||||
li(data-i18n="concepts." + concept)
|
||||
strong
|
||||
span.spr(data-i18n="courses.hours_content")
|
||||
span #{course.get('duration')}
|
||||
.col-md-6.center(style='margin-top: 40px;')
|
||||
if studentMode
|
||||
if enrolledCourses[course.id]
|
||||
a.btn.btn-lg.btn-success.btn-continue(href="/courses/#{course.id}?student=true", data-i18n="common.continue")
|
||||
else
|
||||
button.btn.btn-lg.btn-success.btn-continue(data-toggle='modal', data-target="#continueModal#{course.id}", data-i18n="courses.enter") Enter
|
||||
else if enrolledCourses[course.id]
|
||||
button.btn.btn-lg.btn-success.btn-continue(data-toggle='modal', data-target="#continueModal#{course.id}", data-i18n="common.continue")
|
||||
else if course.get('pricePerSeat') === 0 || me.isAdmin()
|
||||
button.btn.btn-lg.btn-success.btn-buy(data-course-id="#{course.id}", data-i18n='courses.get_free')
|
||||
else
|
||||
button.btn.btn-lg.btn-success.btn-buy(data-course-id="#{course.id}", data-i18n='courses.buy_course')
|
4
app/templates/courses/hour-of-code-view.jade
Normal file
4
app/templates/courses/hour-of-code-view.jade
Normal file
|
@ -0,0 +1,4 @@
|
|||
extends /templates/base
|
||||
|
||||
block content
|
||||
p Hour of Code
|
22
app/templates/courses/student-courses-view.jade
Normal file
22
app/templates/courses/student-courses-view.jade
Normal file
|
@ -0,0 +1,22 @@
|
|||
extends /templates/base
|
||||
|
||||
block content
|
||||
|
||||
p To join a class, ask your teacher for an unlock code.
|
||||
|
||||
.form-horizontal
|
||||
.form-group
|
||||
.col-sm-2
|
||||
button.btn.btn-default.btn-block Join Class
|
||||
.col-sm-6
|
||||
input#classroom-code.form-control(placeholder='<enter unlock code here>')
|
||||
|
||||
.panel.panel-default
|
||||
.panel-heading
|
||||
.panel-title My Courses
|
||||
|
||||
.panel-body.row
|
||||
.col-sm-3 Class 1
|
||||
.col-sm-3 Course 1
|
||||
.col-sm-6
|
||||
button.btn.btn-default.btn-sm Enter
|
10
app/templates/courses/teacher-courses-view.jade
Normal file
10
app/templates/courses/teacher-courses-view.jade
Normal file
|
@ -0,0 +1,10 @@
|
|||
extends /templates/base
|
||||
|
||||
block content
|
||||
|
||||
span *UNDER CONSTRUCTION, please send feedback to
|
||||
a.spl(href='mailto:team@codecombat.com') team@codecombat.com
|
||||
|
||||
hr
|
||||
|
||||
p Teacher courses view
|
|
@ -1,238 +1,8 @@
|
|||
app = require 'core/application'
|
||||
AuthModal = require 'views/core/AuthModal'
|
||||
CocoCollection = require 'collections/CocoCollection'
|
||||
Course = require 'models/Course'
|
||||
CourseInstance = require 'models/CourseInstance'
|
||||
RootView = require 'views/core/RootView'
|
||||
template = require 'templates/courses/courses'
|
||||
utils = require 'core/utils'
|
||||
|
||||
# TODO: Hour of Code (HoC) integration is a mess
|
||||
template = require 'templates/courses/courses-view'
|
||||
|
||||
module.exports = class CoursesView extends RootView
|
||||
id: 'courses-view'
|
||||
template: template
|
||||
|
||||
events:
|
||||
'click .btn-buy': 'onClickBuy'
|
||||
'click .btn-enroll': 'onClickEnroll'
|
||||
'click .btn-enter': 'onClickEnter'
|
||||
'click .btn-hoc-student-continue': 'onClickHOCStudentContinue'
|
||||
'click .btn-student': 'onClickStudent'
|
||||
'click .btn-teacher': 'onClickTeacher'
|
||||
|
||||
constructor: (options) ->
|
||||
super(options)
|
||||
@setUpHourOfCode()
|
||||
@praise = utils.getCoursePraise()
|
||||
@studentMode = Backbone.history.getFragment()?.indexOf('courses/students') >= 0
|
||||
@courses = new CocoCollection([], { url: "/db/course", model: Course})
|
||||
@supermodel.loadCollection(@courses, 'courses')
|
||||
@courseInstances = new CocoCollection([], { url: "/db/user/#{me.id}/course_instances", model: CourseInstance})
|
||||
@listenToOnce @courseInstances, 'sync', @onCourseInstancesLoaded
|
||||
@supermodel.loadCollection(@courseInstances, 'course_instances')
|
||||
if prepaidCode = utils.getQueryVariable('_ppc', false)
|
||||
if me.isAnonymous()
|
||||
@state = 'ppc_logged_out'
|
||||
else
|
||||
@studentMode = true
|
||||
@courseEnrollByURL(prepaidCode)
|
||||
|
||||
setUpHourOfCode: ->
|
||||
# If we are coming in at /hoc, then we show the landing page.
|
||||
# If we have ?hoc=true (for the step after the landing page), then we show any HoC-specific instructions.
|
||||
# If we haven't tracked this player as an hourOfCode player yet, and it's a new account, we do that now.
|
||||
@hocLandingPage = Backbone.history.getFragment()?.indexOf('hoc') >= 0
|
||||
@hocMode = utils.getQueryVariable('hoc', false)
|
||||
elapsed = new Date() - new Date(me.get('dateCreated'))
|
||||
if not me.get('hourOfCode') and (@hocLandingPage or @hocMode) and elapsed < 5 * 60 * 1000
|
||||
me.set('hourOfCode', true)
|
||||
me.patch()
|
||||
$('body').append($('<img src="https://code.org/api/hour/begin_codecombat.png" style="visibility: hidden;">'))
|
||||
application.tracker?.trackEvent 'Hour of Code Begin'
|
||||
if me.get('hourOfCode') and elapsed < 24 * 60 * 60 * 1000
|
||||
@hocMode = true # If they really just arrived, make sure we're still in hocMode even if they lost ?hoc=true.
|
||||
|
||||
getRenderData: ->
|
||||
context = super()
|
||||
context.courses = @courses.models ? []
|
||||
context.enrolledCourses = @enrolledCourses ? {}
|
||||
context.hocLandingPage = @hocLandingPage
|
||||
context.hocMode = @hocMode
|
||||
context.instances = @courseInstances.models ? []
|
||||
context.praise = @praise
|
||||
context.state = @state
|
||||
context.stateMessage = @stateMessage
|
||||
context.studentMode = @studentMode
|
||||
context
|
||||
|
||||
afterRender: ->
|
||||
super()
|
||||
@setupCoursesFAQPopover()
|
||||
|
||||
onCourseInstancesLoaded: ->
|
||||
@enrolledCourses = {}
|
||||
@enrolledCourses[courseInstance.get('courseID')] = true for courseInstance in @courseInstances.models
|
||||
|
||||
setupCoursesFAQPopover: ->
|
||||
popoverTitle = "<h3>" + $.i18n.t('courses.faq') + "<button type='button' class='close' onclick='$('.courses-faq').popover('hide');'>×</button></h3>"
|
||||
popoverContent = "<p><strong>" + $.i18n.t('courses.question') + "</strong> " + $.i18n.t('courses.question1') + "</p>"
|
||||
popoverContent += "<p><strong>" + $.i18n.t('courses.answer') + "</strong> " + $.i18n.t('courses.answer1') + "</p>"
|
||||
popoverContent += "<p>" + $.i18n.t('courses.answer2') + "</p>"
|
||||
@$el.find('.courses-faq').popover(
|
||||
animation: true
|
||||
html: true
|
||||
placement: 'top'
|
||||
trigger: 'click'
|
||||
title: popoverTitle
|
||||
content: popoverContent
|
||||
container: @$el
|
||||
).on 'shown.bs.popover', =>
|
||||
application.tracker?.trackEvent 'Subscription payment methods hover'
|
||||
|
||||
onClickBuy: (e) ->
|
||||
$('.continue-dialog').modal('hide')
|
||||
courseID = $(e.target).data('course-id')
|
||||
route = "/courses/enroll/#{courseID}"
|
||||
viewClass = require 'views/courses/CourseEnrollView'
|
||||
viewArgs = [{}, courseID]
|
||||
navigationEvent = route: route, viewClass: viewClass, viewArgs: viewArgs
|
||||
Backbone.Mediator.publish 'router:navigate', navigationEvent
|
||||
|
||||
onClickEnroll: (e) ->
|
||||
return @openModalView new AuthModal() if me.isAnonymous()
|
||||
courseID = $(e.target).data('course-id')
|
||||
prepaidCode = ($(".code-input[data-course-id=#{courseID}]").val() ? '').trim()
|
||||
@courseEnrollByModal(prepaidCode)
|
||||
|
||||
onClickEnter: (e) ->
|
||||
$('.continue-dialog').modal('hide')
|
||||
courseID = $(e.target).data('course-id')
|
||||
courseInstanceID = $(".select-session[data-course-id=#{courseID}]").val()
|
||||
route = "/courses/#{courseID}/#{courseInstanceID}"
|
||||
viewClass = require 'views/courses/CourseDetailsView'
|
||||
viewArgs = [{}, courseID, courseInstanceID]
|
||||
navigationEvent = route: route, viewClass: viewClass, viewArgs: viewArgs
|
||||
Backbone.Mediator.publish 'router:navigate', navigationEvent
|
||||
|
||||
onClickHOCStudentContinue: (e) ->
|
||||
$('.continue-dialog').modal('hide')
|
||||
if e
|
||||
courseID = $(e.target).data('course-id')
|
||||
else
|
||||
courseID = '560f1a9f22961295f9427742'
|
||||
|
||||
@state = 'enrolling'
|
||||
@stateMessage = undefined
|
||||
@render?()
|
||||
|
||||
# TODO: Copied from CourseEnrollView
|
||||
|
||||
data =
|
||||
name: 'Single Player'
|
||||
seats: 9999
|
||||
courseID: courseID
|
||||
hourOfCode: true
|
||||
jqxhr = $.post('/db/course_instance/-/create', data)
|
||||
jqxhr.done (data, textStatus, jqXHR) =>
|
||||
application.tracker?.trackEvent 'Finished HoC student course creation', {courseID: courseID}
|
||||
# TODO: handle fetch errors
|
||||
me.fetch(cache: false).always =>
|
||||
courseID = courseID
|
||||
route = "/courses/#{courseID}"
|
||||
viewArgs = [{}, courseID]
|
||||
if data?.length > 0
|
||||
courseInstanceID = data[0]._id
|
||||
route += "/#{courseInstanceID}"
|
||||
viewArgs[0].courseInstanceID = courseInstanceID
|
||||
Backbone.Mediator.publish 'router:navigate',
|
||||
route: route
|
||||
viewClass: 'views/courses/CourseDetailsView'
|
||||
viewArgs: viewArgs
|
||||
jqxhr.fail (xhr, textStatus, errorThrown) =>
|
||||
console.error 'Got an error purchasing a course:', textStatus, errorThrown
|
||||
application.tracker?.trackEvent 'Failed HoC student course creation', status: textStatus
|
||||
if xhr.status is 402
|
||||
@state = 'declined'
|
||||
@stateMessage = arguments[2]
|
||||
else
|
||||
@state = 'unknown_error'
|
||||
@stateMessage = "#{xhr.status}: #{xhr.responseText}"
|
||||
@render?()
|
||||
|
||||
onClickStudent: (e) ->
|
||||
if @supermodel.finished() and @hocLandingPage
|
||||
# Automatically enroll in first course
|
||||
@onClickHOCStudentContinue()
|
||||
return
|
||||
route = "/courses/students"
|
||||
route += "?hoc=true" if @hocLandingPage or @hocMode
|
||||
viewClass = require 'views/courses/CoursesView'
|
||||
navigationEvent = route: route, viewClass: viewClass, viewArgs: []
|
||||
Backbone.Mediator.publish 'router:navigate', navigationEvent
|
||||
|
||||
onClickTeacher: (e) ->
|
||||
route = "/courses/teachers"
|
||||
route += "?hoc=true" if @hocLandingPage or @hocMode
|
||||
viewClass = require 'views/courses/CoursesView'
|
||||
navigationEvent = route: route, viewClass: viewClass, viewArgs: []
|
||||
Backbone.Mediator.publish 'router:navigate', navigationEvent
|
||||
|
||||
courseEnrollByURL: (prepaidCode) ->
|
||||
@state = 'enrolling'
|
||||
@render?()
|
||||
$.ajax({
|
||||
method: 'POST'
|
||||
url: '/db/course_instance/-/redeem_prepaid'
|
||||
data: prepaidCode: prepaidCode
|
||||
context: @
|
||||
success: @onRedeemPrepaidSuccess
|
||||
error: (xhr, textStatus, errorThrown) ->
|
||||
console.error 'Got an error redeeming a course prepaid code:', textStatus, errorThrown
|
||||
application.tracker?.trackEvent 'Failed to redeem course prepaid code by url', status: textStatus
|
||||
@state = 'unknown_error'
|
||||
@stateMessage = "Failed to redeem code: #{xhr.responseText}"
|
||||
@render?()
|
||||
})
|
||||
|
||||
courseEnrollByModal: (prepaidCode) ->
|
||||
@state = 'enrolling-by-modal'
|
||||
@renderSelectors '.student-dialog-state-row'
|
||||
$.ajax({
|
||||
method: 'POST'
|
||||
url: '/db/course_instance/-/redeem_prepaid'
|
||||
data: prepaidCode: prepaidCode
|
||||
context: @
|
||||
success: ->
|
||||
$('.continue-dialog').modal('hide')
|
||||
@onRedeemPrepaidSuccess(arguments...)
|
||||
error: (jqxhr, textStatus, errorThrown) ->
|
||||
application.tracker?.trackEvent 'Failed to redeem course prepaid code by modal', status: textStatus
|
||||
@state = 'unknown_error'
|
||||
if jqxhr.status is 422
|
||||
@stateMessage = 'Please enter a code.'
|
||||
else if jqxhr.status is 404
|
||||
@stateMessage = 'Code not found.'
|
||||
else
|
||||
@stateMessage = "#{jqxhr.responseText}"
|
||||
@renderSelectors '.student-dialog-state-row'
|
||||
})
|
||||
|
||||
onRedeemPrepaidSuccess: (data, textStatus, jqxhr) ->
|
||||
prepaidID = data[0]?.prepaidID
|
||||
application.tracker?.trackEvent 'Redeemed course prepaid code', {prepaidCode: prepaidID}
|
||||
me.fetch(cache: false).always =>
|
||||
if data?.length > 0 && data[0].courseID && data[0]._id
|
||||
courseID = data[0].courseID
|
||||
courseInstanceID = data[0]._id
|
||||
route = "/courses/#{courseID}/#{courseInstanceID}"
|
||||
viewArgs = [{}, courseID, courseInstanceID]
|
||||
Backbone.Mediator.publish 'router:navigate',
|
||||
route: route
|
||||
viewClass: 'views/courses/CourseDetailsView'
|
||||
viewArgs: viewArgs
|
||||
else
|
||||
@state = 'unknown_error'
|
||||
@stateMessage = "Database error."
|
||||
@render?()
|
||||
|
||||
|
|
77
app/views/courses/HourOfCodeView.coffee
Normal file
77
app/views/courses/HourOfCodeView.coffee
Normal file
|
@ -0,0 +1,77 @@
|
|||
app = require 'core/application'
|
||||
AuthModal = require 'views/core/AuthModal'
|
||||
CocoCollection = require 'collections/CocoCollection'
|
||||
Course = require 'models/Course'
|
||||
CourseInstance = require 'models/CourseInstance'
|
||||
RootView = require 'views/core/RootView'
|
||||
template = require 'templates/courses/hour-of-code-view'
|
||||
utils = require 'core/utils'
|
||||
|
||||
|
||||
module.exports = class HourOfCodeView extends RootView
|
||||
id: 'hour-of-code-view'
|
||||
template: template
|
||||
|
||||
constructor: (options) ->
|
||||
super(options)
|
||||
# @setUpHourOfCode()
|
||||
#
|
||||
# setUpHourOfCode: ->
|
||||
# # If we are coming in at /hoc, then we show the landing page.
|
||||
# # If we have ?hoc=true (for the step after the landing page), then we show any HoC-specific instructions.
|
||||
# # If we haven't tracked this player as an hourOfCode player yet, and it's a new account, we do that now.
|
||||
# @hocLandingPage = Backbone.history.getFragment()?.indexOf('hoc') >= 0
|
||||
# @hocMode = utils.getQueryVariable('hoc', false)
|
||||
# elapsed = new Date() - new Date(me.get('dateCreated'))
|
||||
# if not me.get('hourOfCode') and (@hocLandingPage or @hocMode) and elapsed < 5 * 60 * 1000
|
||||
# me.set('hourOfCode', true)
|
||||
# me.patch()
|
||||
# $('body').append($('<img src="https://code.org/api/hour/begin_codecombat.png" style="visibility: hidden;">'))
|
||||
# application.tracker?.trackEvent 'Hour of Code Begin'
|
||||
# if me.get('hourOfCode') and elapsed < 24 * 60 * 60 * 1000
|
||||
# @hocMode = true # If they really just arrived, make sure we're still in hocMode even if they lost ?hoc=true.
|
||||
#
|
||||
# onClickHOCStudentContinue: (e) ->
|
||||
# $('.continue-dialog').modal('hide')
|
||||
# if e
|
||||
# courseID = $(e.target).data('course-id')
|
||||
# else
|
||||
# courseID = '560f1a9f22961295f9427742'
|
||||
#
|
||||
# @state = 'enrolling'
|
||||
# @stateMessage = undefined
|
||||
# @render?()
|
||||
#
|
||||
# # TODO: Copied from CourseEnrollView
|
||||
#
|
||||
# data =
|
||||
# name: 'Single Player'
|
||||
# seats: 9999
|
||||
# courseID: courseID
|
||||
# hourOfCode: true
|
||||
# jqxhr = $.post('/db/course_instance/-/create', data)
|
||||
# jqxhr.done (data, textStatus, jqXHR) =>
|
||||
# application.tracker?.trackEvent 'Finished HoC student course creation', {courseID: courseID}
|
||||
# # TODO: handle fetch errors
|
||||
# me.fetch(cache: false).always =>
|
||||
# courseID = courseID
|
||||
# route = "/courses/#{courseID}"
|
||||
# viewArgs = [{}, courseID]
|
||||
# if data?.length > 0
|
||||
# courseInstanceID = data[0]._id
|
||||
# route += "/#{courseInstanceID}"
|
||||
# viewArgs[0].courseInstanceID = courseInstanceID
|
||||
# Backbone.Mediator.publish 'router:navigate',
|
||||
# route: route
|
||||
# viewClass: 'views/courses/CourseDetailsView'
|
||||
# viewArgs: viewArgs
|
||||
# jqxhr.fail (xhr, textStatus, errorThrown) =>
|
||||
# console.error 'Got an error purchasing a course:', textStatus, errorThrown
|
||||
# application.tracker?.trackEvent 'Failed HoC student course creation', status: textStatus
|
||||
# if xhr.status is 402
|
||||
# @state = 'declined'
|
||||
# @stateMessage = arguments[2]
|
||||
# else
|
||||
# @state = 'unknown_error'
|
||||
# @stateMessage = "#{xhr.status}: #{xhr.responseText}"
|
||||
# @render?()
|
74
app/views/courses/StudentCoursesView.coffee
Normal file
74
app/views/courses/StudentCoursesView.coffee
Normal file
|
@ -0,0 +1,74 @@
|
|||
app = require 'core/application'
|
||||
AuthModal = require 'views/core/AuthModal'
|
||||
CocoCollection = require 'collections/CocoCollection'
|
||||
Course = require 'models/Course'
|
||||
CourseInstance = require 'models/CourseInstance'
|
||||
RootView = require 'views/core/RootView'
|
||||
template = require 'templates/courses/student-courses-view'
|
||||
utils = require 'core/utils'
|
||||
|
||||
# TODO: Implement join class
|
||||
# TODO: Implement course instance links
|
||||
|
||||
module.exports = class StudentCoursesView extends RootView
|
||||
id: 'student-courses-view'
|
||||
template: template
|
||||
|
||||
events:
|
||||
'click #join-class-btn': 'onClickJoinClassButton'
|
||||
|
||||
constructor: (options) ->
|
||||
super(options)
|
||||
@courseInstances = new CocoCollection([], { url: "/db/user/#{me.id}/course_instances", model: CourseInstance})
|
||||
@supermodel.loadCollection(@courseInstances, 'course_instances')
|
||||
|
||||
# if classCode = utils.getQueryVariable('_cc', false) and not me.isAnonymous()
|
||||
# @joinClass(classCode)
|
||||
#
|
||||
# onClickJoinClassButton: (e) ->
|
||||
# return @openModalView new AuthModal() if me.isAnonymous()
|
||||
# courseID = $(e.target).data('course-id')
|
||||
# classCode = ($(".code-input[data-course-id=#{courseID}]").val() ? '').trim()
|
||||
# @courseEnrollByModal(prepaidCode)
|
||||
#
|
||||
# joinClass: (prepaidCode) ->
|
||||
# @state = 'enrolling-by-modal'
|
||||
# @renderSelectors '.student-dialog-state-row'
|
||||
# $.ajax({
|
||||
# method: 'POST'
|
||||
# url: '/db/course_instance/-/redeem_prepaid'
|
||||
# data: prepaidCode: prepaidCode
|
||||
# context: @
|
||||
# success: ->
|
||||
# $('.continue-dialog').modal('hide')
|
||||
# @onRedeemPrepaidSuccess(arguments...)
|
||||
# error: (jqxhr, textStatus, errorThrown) ->
|
||||
# application.tracker?.trackEvent 'Failed to redeem course prepaid code by modal', status: textStatus
|
||||
# @state = 'unknown_error'
|
||||
# if jqxhr.status is 422
|
||||
# @stateMessage = 'Please enter a code.'
|
||||
# else if jqxhr.status is 404
|
||||
# @stateMessage = 'Code not found.'
|
||||
# else
|
||||
# @stateMessage = "#{jqxhr.responseText}"
|
||||
# @renderSelectors '.student-dialog-state-row'
|
||||
# })
|
||||
#
|
||||
# onRedeemPrepaidSuccess: (data, textStatus, jqxhr) ->
|
||||
# prepaidID = data[0]?.prepaidID
|
||||
# application.tracker?.trackEvent 'Redeemed course prepaid code', {prepaidCode: prepaidID}
|
||||
# me.fetch(cache: false).always =>
|
||||
# if data?.length > 0 && data[0].courseID && data[0]._id
|
||||
# courseID = data[0].courseID
|
||||
# courseInstanceID = data[0]._id
|
||||
# route = "/courses/#{courseID}/#{courseInstanceID}"
|
||||
# viewArgs = [{}, courseID, courseInstanceID]
|
||||
# Backbone.Mediator.publish 'router:navigate',
|
||||
# route: route
|
||||
# viewClass: 'views/courses/CourseDetailsView'
|
||||
# viewArgs: viewArgs
|
||||
# else
|
||||
# @state = 'unknown_error'
|
||||
# @stateMessage = "Database error."
|
||||
# @render?()
|
||||
|
24
app/views/courses/TeacherCoursesView.coffee
Normal file
24
app/views/courses/TeacherCoursesView.coffee
Normal file
|
@ -0,0 +1,24 @@
|
|||
app = require 'core/application'
|
||||
AuthModal = require 'views/core/AuthModal'
|
||||
CocoCollection = require 'collections/CocoCollection'
|
||||
Course = require 'models/Course'
|
||||
Classroom = require 'models/Classroom'
|
||||
CourseInstance = require 'models/CourseInstance'
|
||||
RootView = require 'views/core/RootView'
|
||||
template = require 'templates/courses/teacher-courses-view'
|
||||
utils = require 'core/utils'
|
||||
|
||||
#
|
||||
|
||||
module.exports = class TeacherCoursesView extends RootView
|
||||
id: 'teacher-courses-view'
|
||||
template: template
|
||||
|
||||
constructor: (options) ->
|
||||
super(options)
|
||||
@courses = new CocoCollection([], { url: "/db/course", model: Course})
|
||||
@supermodel.loadCollection(@courses, 'courses')
|
||||
@classrooms = new CocoCollection([], { url: "/db/classroom", model: Classroom })
|
||||
@listenToOnce @classrooms, 'sync', @onCourseInstancesLoaded
|
||||
@supermodel.loadCollection(@classrooms, 'classrooms', {data: {ownerID: me.id}})
|
||||
@
|
Loading…
Reference in a new issue