mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-05-03 17:33:31 -04:00
Update /courses/teachers
Adding edit class settings modal Adding create new class modal Repurposing ClassroomSettingsModal for create new class too.
This commit is contained in:
parent
e8fc90de23
commit
171e4f888e
5 changed files with 46 additions and 193 deletions
app
locale
templates/courses
views/courses
|
@ -879,6 +879,7 @@
|
||||||
courses:
|
courses:
|
||||||
course: "Course"
|
course: "Course"
|
||||||
courses: "courses"
|
courses: "courses"
|
||||||
|
create_new_class: "Create New Class"
|
||||||
not_enrolled: "You are not enrolled in this course."
|
not_enrolled: "You are not enrolled in this course."
|
||||||
visit_pref: "Please visit the"
|
visit_pref: "Please visit the"
|
||||||
visit_suf: "page to enroll."
|
visit_suf: "page to enroll."
|
||||||
|
|
|
@ -1,24 +1,30 @@
|
||||||
extends /templates/core/modal-base
|
extends /templates/core/modal-base
|
||||||
|
|
||||||
block modal-header-content
|
block modal-header-content
|
||||||
button.close(data-dismiss='modal')
|
if view.classroom
|
||||||
span ×
|
h3.modal-title(data-i18n="courses.edit_settings1")
|
||||||
h3.modal-title(data-i18n="courses.edit_settings1")
|
else
|
||||||
|
h3.modal-title(data-i18n="courses.create_new_class")
|
||||||
|
|
||||||
block modal-body-content
|
block modal-body-content
|
||||||
.form
|
.form
|
||||||
.form-group
|
.form-group
|
||||||
label(data-i18n="courses.title")
|
label(data-i18n="courses.title")
|
||||||
input.form-control.settings-name-input(type='text', value="#{view.classroom.get('name') || ''}")
|
- var name = view.classroom && view.classroom.get('name') ? view.classroom.get('name') : '';
|
||||||
|
input.form-control.settings-name-input(type='text', value="#{name}")
|
||||||
.form-group
|
.form-group
|
||||||
label(data-i18n="courses.description")
|
label(data-i18n="courses.description")
|
||||||
textarea.form-control.settings-description-input(rows=2)= view.classroom.get('description')
|
- var description = view.classroom && view.classroom.get('description') ? view.classroom.get('description') : '';
|
||||||
|
textarea.form-control.settings-description-input(rows=2)= description
|
||||||
.form-group
|
.form-group
|
||||||
label(data-i18n="choose_hero.programming_language")
|
label(data-i18n="choose_hero.programming_language")
|
||||||
select.form-control#programming-language-select
|
select.form-control#programming-language-select
|
||||||
- var aceConfig = view.classroom.get('aceConfig') || {};
|
- var aceConfig = view.classroom ? view.classroom.get('aceConfig') || {} : {};
|
||||||
option(value="python", selected=aceConfig.language==='python') Python
|
option(value="python", selected=aceConfig.language==='python') Python
|
||||||
option(value="javascript", selected=aceConfig.language==='javascript') JavaScript
|
option(value="javascript", selected=aceConfig.language==='javascript') JavaScript
|
||||||
|
|
||||||
block modal-footer-content
|
block modal-footer-content
|
||||||
button#save-settings-btn.btn(data-i18n="common.save_changes")
|
if view.classroom
|
||||||
|
button#save-settings-btn.btn(data-i18n="common.save_changes")
|
||||||
|
else
|
||||||
|
button#save-settings-btn.btn(data-i18n="courses.create_class")
|
||||||
|
|
|
@ -19,7 +19,7 @@ block content
|
||||||
.no-students No classes yet!
|
.no-students No classes yet!
|
||||||
|
|
||||||
.text-center
|
.text-center
|
||||||
button.btn.btn-lg.btn-success.uppercase create new class
|
button.btn.btn-lg.btn-success.uppercase.create-new-class create new class
|
||||||
|
|
||||||
h3 Available Courses
|
h3 Available Courses
|
||||||
|
|
||||||
|
@ -47,29 +47,29 @@ block footer
|
||||||
|
|
||||||
mixin classroom(classroom)
|
mixin classroom(classroom)
|
||||||
.row
|
.row
|
||||||
- var classMemberCount = classroom.get('members').length
|
- var classMemberCount = classroom.get('members') ? classroom.get('members').length : 0;
|
||||||
if classMemberCount > 0
|
if classMemberCount > 0
|
||||||
.col-md-8
|
.col-md-8
|
||||||
p
|
p
|
||||||
span.spr.class-name= classroom.get('name')
|
span.spr.class-name= classroom.get('name')
|
||||||
if classroom.get('aceConfig') && classroom.get('aceConfig').language
|
if classroom.get('aceConfig') && classroom.get('aceConfig').language === 'javascript'
|
||||||
span.spr (#{classroom.get('aceConfig').language})
|
span.spr.class-name (JavaScript)
|
||||||
else
|
else
|
||||||
span.spr.class-name (Python)
|
span.spr.class-name (Python)
|
||||||
a edit class details
|
a.edit-classroom-small(data-i18n="courses.edit_settings", data-classroom-id="#{classroom.id}")
|
||||||
.active-courses active courses
|
.active-courses active courses
|
||||||
- var courseInstances = view.courseInstances.where({classroomID: classroom.id})
|
- var courseInstances = view.courseInstances.where({classroomID: classroom.id});
|
||||||
each courseInstance in courseInstances
|
each courseInstance in courseInstances
|
||||||
+course(courseInstance, classMemberCount)
|
+course(courseInstance, classMemberCount)
|
||||||
else
|
else
|
||||||
.col-md-12
|
.col-md-12
|
||||||
p
|
p
|
||||||
span.spr.class-name= classroom.get('name')
|
span.spr.class-name= classroom.get('name')
|
||||||
if classroom.get('aceConfig') && classroom.get('aceConfig').language
|
if classroom.get('aceConfig') && classroom.get('aceConfig').language === 'javascript'
|
||||||
span.spr (#{classroom.get('aceConfig').language})
|
span.spr.class-name (JavaScript)
|
||||||
else
|
else
|
||||||
span.spr.class-name (Python)
|
span.spr.class-name (Python)
|
||||||
a edit class details
|
a.edit-classroom-small(data-i18n="courses.edit_settings", data-classroom-id="#{classroom.id}")
|
||||||
.no-students No students yet!
|
.no-students No students yet!
|
||||||
.text-center
|
.text-center
|
||||||
a.btn.btn-info.uppercase(href='/courses/#{classroom.id}') add students
|
a.btn.btn-info.uppercase(href='/courses/#{classroom.id}') add students
|
||||||
|
@ -84,9 +84,9 @@ mixin classroom(classroom)
|
||||||
.divider
|
.divider
|
||||||
|
|
||||||
mixin course(courseInstance, classMemberCount)
|
mixin course(courseInstance, classMemberCount)
|
||||||
- var courseMemberCount = courseInstance.get('members').length
|
- var courseMemberCount = courseInstance.get('members') ? courseInstance.get('members').length : 0;
|
||||||
if courseMemberCount > 0
|
if courseMemberCount > 0
|
||||||
- var course = view.courses.get(courseInstance.get('courseID'))
|
- var course = view.courses.get(courseInstance.get('courseID'));
|
||||||
p
|
p
|
||||||
.course-name= course.get('name')
|
.course-name= course.get('name')
|
||||||
.course-enrolled #{courseMemberCount} / #{classMemberCount} students enrolled
|
.course-enrolled #{courseMemberCount} / #{classMemberCount} students enrolled
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
|
Classroom = require 'models/Classroom'
|
||||||
ModalView = require 'views/core/ModalView'
|
ModalView = require 'views/core/ModalView'
|
||||||
template = require 'templates/courses/classroom-settings-modal'
|
template = require 'templates/courses/classroom-settings-modal'
|
||||||
|
|
||||||
module.exports = class AddLevelSystemModal extends ModalView
|
module.exports = class AddLevelSystemModal extends ModalView
|
||||||
id: 'classroom-settings-modal'
|
id: 'classroom-settings-modal'
|
||||||
template: template
|
template: template
|
||||||
|
|
||||||
events:
|
events:
|
||||||
'click #save-settings-btn': 'onClickSaveSettingsButton'
|
'click #save-settings-btn': 'onClickSaveSettingsButton'
|
||||||
|
|
||||||
|
@ -12,15 +13,16 @@ module.exports = class AddLevelSystemModal extends ModalView
|
||||||
@classroom = options.classroom
|
@classroom = options.classroom
|
||||||
|
|
||||||
onClickSaveSettingsButton: ->
|
onClickSaveSettingsButton: ->
|
||||||
return unless @classroom
|
name = $('.settings-name-input').val()
|
||||||
if name = $('.settings-name-input').val()
|
unless @classroom
|
||||||
|
return unless name
|
||||||
|
@classroom = new Classroom({ name: name })
|
||||||
|
if name
|
||||||
@classroom.set('name', name)
|
@classroom.set('name', name)
|
||||||
description = $('.settings-description-input').val()
|
description = $('.settings-description-input').val()
|
||||||
@classroom.set('description', description)
|
@classroom.set('description', description)
|
||||||
@classroom.set('aceConfig', {
|
@classroom.set('aceConfig', {
|
||||||
language: @$('#programming-language-select').val()
|
language: @$('#programming-language-select').val()
|
||||||
})
|
})
|
||||||
@classroom.patch()
|
@classroom.save()
|
||||||
@hide()
|
@hide()
|
||||||
|
|
||||||
|
|
|
@ -5,12 +5,9 @@ CocoModel = require 'models/CocoModel'
|
||||||
Course = require 'models/Course'
|
Course = require 'models/Course'
|
||||||
Classroom = require 'models/Classroom'
|
Classroom = require 'models/Classroom'
|
||||||
User = require 'models/User'
|
User = require 'models/User'
|
||||||
Prepaid = require 'models/Prepaid'
|
|
||||||
CourseInstance = require 'models/CourseInstance'
|
CourseInstance = require 'models/CourseInstance'
|
||||||
RootView = require 'views/core/RootView'
|
RootView = require 'views/core/RootView'
|
||||||
template = require 'templates/courses/teacher-courses-view'
|
template = require 'templates/courses/teacher-courses-view'
|
||||||
utils = require 'core/utils'
|
|
||||||
InviteToClassroomModal = require 'views/courses/InviteToClassroomModal'
|
|
||||||
ClassroomSettingsModal = require 'views/courses/ClassroomSettingsModal'
|
ClassroomSettingsModal = require 'views/courses/ClassroomSettingsModal'
|
||||||
|
|
||||||
module.exports = class TeacherCoursesView extends RootView
|
module.exports = class TeacherCoursesView extends RootView
|
||||||
|
@ -18,11 +15,7 @@ module.exports = class TeacherCoursesView extends RootView
|
||||||
template: template
|
template: template
|
||||||
|
|
||||||
events:
|
events:
|
||||||
'click #create-new-class-btn': 'onClickCreateNewclassButton'
|
'click .create-new-class': 'onClickCreateNewClassButton'
|
||||||
'click .add-students-btn': 'onClickAddStudentsButton'
|
|
||||||
'click .course-instance-membership-checkbox': 'onClickCourseInstanceMembershipCheckbox'
|
|
||||||
'click #save-changes-btn': 'onClickSaveChangesButton'
|
|
||||||
'click #manage-tab-link': 'onClickManageTabLink'
|
|
||||||
'click .edit-classroom-small': 'onClickEditClassroomSmall'
|
'click .edit-classroom-small': 'onClickEditClassroomSmall'
|
||||||
|
|
||||||
constructor: (options) ->
|
constructor: (options) ->
|
||||||
|
@ -38,15 +31,7 @@ module.exports = class TeacherCoursesView extends RootView
|
||||||
@courseInstances.sliceWithMembers = -> return @filter (courseInstance) -> _.size(courseInstance.get('members')) and courseInstance.get('classroomID')
|
@courseInstances.sliceWithMembers = -> return @filter (courseInstance) -> _.size(courseInstance.get('members')) and courseInstance.get('classroomID')
|
||||||
@supermodel.loadCollection(@courseInstances, 'course_instances', {data: {ownerID: me.id}})
|
@supermodel.loadCollection(@courseInstances, 'course_instances', {data: {ownerID: me.id}})
|
||||||
@members = new CocoCollection([], { model: User })
|
@members = new CocoCollection([], { model: User })
|
||||||
@prepaids = new CocoCollection([], { url: "/db/prepaid", model: Prepaid })
|
@listenTo @members, 'sync', @render
|
||||||
sum = (numbers) -> _.reduce(numbers, (a, b) -> a + b)
|
|
||||||
@prepaids.totalMaxRedeemers = -> sum((prepaid.get('maxRedeemers') for prepaid in @models)) or 0
|
|
||||||
@prepaids.totalRedeemers = -> sum((_.size(prepaid.get('redeemers')) for prepaid in @models)) or 0
|
|
||||||
@prepaids.comparator = '_id'
|
|
||||||
@supermodel.loadCollection(@prepaids, 'prepaids', {data: {creator: me.id}})
|
|
||||||
@listenTo @members, 'sync', @renderManageTab
|
|
||||||
@usersToRedeem = new CocoCollection([], { model: User })
|
|
||||||
@hoc = utils.getQueryVariable('hoc')
|
|
||||||
@
|
@
|
||||||
|
|
||||||
onceClassroomsSync: ->
|
onceClassroomsSync: ->
|
||||||
|
@ -56,160 +41,19 @@ module.exports = class TeacherCoursesView extends RootView
|
||||||
url: "/db/classroom/#{classroom.id}/members"
|
url: "/db/classroom/#{classroom.id}/members"
|
||||||
})
|
})
|
||||||
|
|
||||||
onClickCreateNewclassButton: ->
|
onClickCreateNewClassButton: ->
|
||||||
name = @$('#new-classroom-name-input').val()
|
return @openModalView new AuthModal() if me.get('anonymous')
|
||||||
return unless name
|
modal = new ClassroomSettingsModal({})
|
||||||
classroom = new Classroom({ name: name })
|
@openModalView(modal)
|
||||||
classroom.save()
|
@listenToOnce modal, 'hide', =>
|
||||||
@classrooms.add(classroom)
|
# TODO: how to get new classroom from modal?
|
||||||
classroom.saving = true
|
@classrooms.add(modal.classroom)
|
||||||
@renderManageTab()
|
# TODO: will this definitely fire after modal saves new classroom?
|
||||||
@listenTo classroom, 'sync', ->
|
@listenToOnce modal.classroom, 'sync', @render
|
||||||
classroom.saving = false
|
|
||||||
@fillMissingCourseInstances()
|
|
||||||
|
|
||||||
renderManageTab: ->
|
|
||||||
isActive = @$('#manage-tab-pane').hasClass('active')
|
|
||||||
@renderSelectors('#manage-tab-pane')
|
|
||||||
@$('#manage-tab-pane').toggleClass('active', isActive)
|
|
||||||
|
|
||||||
onClickEditClassroomSmall: (e) ->
|
onClickEditClassroomSmall: (e) ->
|
||||||
classroomID = $(e.target).closest('small').data('classroom-id')
|
classroomID = $(e.target).data('classroom-id')
|
||||||
classroom = @classrooms.get(classroomID)
|
classroom = @classrooms.get(classroomID)
|
||||||
modal = new ClassroomSettingsModal({classroom: classroom})
|
modal = new ClassroomSettingsModal({classroom: classroom})
|
||||||
@openModalView(modal)
|
@openModalView(modal)
|
||||||
@listenToOnce modal, 'hide', @renderManageTab
|
@listenToOnce modal, 'hide', @render
|
||||||
|
|
||||||
onClickAddStudentsButton: (e) ->
|
|
||||||
classroomID = $(e.target).data('classroom-id')
|
|
||||||
classroom = @classrooms.get(classroomID)
|
|
||||||
modal = new InviteToClassroomModal({classroom: classroom})
|
|
||||||
@openModalView(modal)
|
|
||||||
|
|
||||||
onLoaded: ->
|
|
||||||
super()
|
|
||||||
@linkCourseIntancesToCourses()
|
|
||||||
@fillMissingCourseInstances()
|
|
||||||
|
|
||||||
linkCourseIntancesToCourses: ->
|
|
||||||
for courseInstance in @courseInstances.models
|
|
||||||
courseInstance.course = @courses.get(courseInstance.get('courseID'))
|
|
||||||
|
|
||||||
fillMissingCourseInstances: ->
|
|
||||||
# TODO: Give teachers control over which courses are enabled for a given class.
|
|
||||||
# Add/remove course instances and columns in the view to match.
|
|
||||||
for classroom in @classrooms.models
|
|
||||||
classroom.filling = false
|
|
||||||
for course in @courses.models
|
|
||||||
courseInstance = @courseInstances.findWhere({classroomID: classroom.id, courseID: course.id})
|
|
||||||
if not courseInstance
|
|
||||||
classroom.filling = true
|
|
||||||
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})
|
|
||||||
courseInstance.course = course
|
|
||||||
@courseInstances.add(courseInstance)
|
|
||||||
@listenToOnce courseInstance, 'sync', @fillMissingCourseInstances
|
|
||||||
@renderManageTab()
|
|
||||||
return
|
|
||||||
@renderManageTab()
|
|
||||||
|
|
||||||
onClickCourseInstanceMembershipCheckbox: ->
|
|
||||||
usersToRedeem = {}
|
|
||||||
checkedBoxes = @$('.course-instance-membership-checkbox:checked')
|
|
||||||
_.each checkedBoxes, (el) =>
|
|
||||||
$el = $(el)
|
|
||||||
userID = $el.data('user-id')
|
|
||||||
return if usersToRedeem[userID]
|
|
||||||
user = @members.get(userID)
|
|
||||||
return if user.get('coursePrepaidID')
|
|
||||||
courseInstanceID = $el.data('course-instance-id')
|
|
||||||
courseInstance = @courseInstances.get(courseInstanceID)
|
|
||||||
return if courseInstance.course.get('free')
|
|
||||||
usersToRedeem[userID] = user
|
|
||||||
|
|
||||||
@usersToRedeem = new CocoCollection(_.values(usersToRedeem), {model: User})
|
|
||||||
@numCourseInstancesToAddTo = checkedBoxes.length
|
|
||||||
@renderSelectors '#fixed-area'
|
|
||||||
|
|
||||||
onClickSaveChangesButton: ->
|
|
||||||
@$('.course-instance-membership-checkbox').attr('disabled', true)
|
|
||||||
checkedBoxes = @$('.course-instance-membership-checkbox:checked')
|
|
||||||
raw = _.map checkedBoxes, (el) =>
|
|
||||||
$el = $(el)
|
|
||||||
userID = $el.data('user-id')
|
|
||||||
courseInstanceID = $el.data('course-instance-id')
|
|
||||||
courseInstance = @courseInstances.get(courseInstanceID)
|
|
||||||
return {
|
|
||||||
courseInstance: courseInstance
|
|
||||||
userID: userID
|
|
||||||
}
|
|
||||||
@membershipAdditions = new CocoCollection(raw, { model: User }) # TODO: Allow collections not to have models defined?
|
|
||||||
@membershipAdditions.originalSize = @membershipAdditions.size()
|
|
||||||
@usersToRedeem.originalSize = @usersToRedeem.size()
|
|
||||||
@state = 'saving-changes'
|
|
||||||
@renderSelectors '#fixed-area'
|
|
||||||
@redeemUsers()
|
|
||||||
|
|
||||||
redeemUsers: ->
|
|
||||||
if not @usersToRedeem.size()
|
|
||||||
@addMemberships()
|
|
||||||
return
|
|
||||||
|
|
||||||
user = @usersToRedeem.first()
|
|
||||||
|
|
||||||
prepaid = @prepaids.find((prepaid) -> prepaid.get('properties').endDate? and prepaid.openSpots())
|
|
||||||
prepaid = @prepaids.find((prepaid) -> prepaid.openSpots()) unless prepaid
|
|
||||||
$.ajax({
|
|
||||||
method: 'POST'
|
|
||||||
url: _.result(prepaid, 'url') + '/redeemers'
|
|
||||||
data: { userID: user.id }
|
|
||||||
context: @
|
|
||||||
success: ->
|
|
||||||
@usersToRedeem.remove(user)
|
|
||||||
@renderSelectors '#fixed-area'
|
|
||||||
@redeemUsers()
|
|
||||||
error: (jqxhr, textStatus, errorThrown) ->
|
|
||||||
if jqxhr.status is 402
|
|
||||||
@state = 'error'
|
|
||||||
@stateMessage = arguments[2]
|
|
||||||
else
|
|
||||||
@state = 'error'
|
|
||||||
@stateMessage = "#{jqxhr.status}: #{jqxhr.responseText}"
|
|
||||||
@renderSelectors '#fixed-area'
|
|
||||||
})
|
|
||||||
|
|
||||||
addMemberships: ->
|
|
||||||
if not @membershipAdditions.size()
|
|
||||||
@renderSelectors '#fixed-area'
|
|
||||||
document.location.reload()
|
|
||||||
return
|
|
||||||
|
|
||||||
membershipAddition = @membershipAdditions.first()
|
|
||||||
courseInstance = membershipAddition.get('courseInstance')
|
|
||||||
userID = membershipAddition.get('userID')
|
|
||||||
$.ajax({
|
|
||||||
method: 'POST'
|
|
||||||
url: _.result(courseInstance, 'url') + '/members'
|
|
||||||
data: { userID: userID }
|
|
||||||
context: @
|
|
||||||
success: ->
|
|
||||||
@membershipAdditions.remove(membershipAddition)
|
|
||||||
@renderSelectors '#fixed-area'
|
|
||||||
@addMemberships()
|
|
||||||
error: (jqxhr, textStatus, errorThrown) ->
|
|
||||||
if jqxhr.status is 402
|
|
||||||
@state = 'error'
|
|
||||||
@stateMessage = arguments[2]
|
|
||||||
else
|
|
||||||
@state = 'error'
|
|
||||||
@stateMessage = "#{jqxhr.status}: #{jqxhr.responseText}"
|
|
||||||
@renderSelectors '#fixed-area'
|
|
||||||
})
|
|
||||||
|
|
||||||
onClickManageTabLink: ->
|
|
||||||
@$('.nav-tabs a[href="#manage-tab-pane"]').tab('show')
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue