diff --git a/app/locale/en.coffee b/app/locale/en.coffee index b59bf711b..6a827f010 100644 --- a/app/locale/en.coffee +++ b/app/locale/en.coffee @@ -879,6 +879,7 @@ courses: course: "Course" courses: "courses" + create_new_class: "Create New Class" not_enrolled: "You are not enrolled in this course." visit_pref: "Please visit the" visit_suf: "page to enroll." diff --git a/app/templates/courses/classroom-settings-modal.jade b/app/templates/courses/classroom-settings-modal.jade index 88064bcd1..fdcb0ee40 100644 --- a/app/templates/courses/classroom-settings-modal.jade +++ b/app/templates/courses/classroom-settings-modal.jade @@ -1,24 +1,30 @@ extends /templates/core/modal-base block modal-header-content - button.close(data-dismiss='modal') - span × - h3.modal-title(data-i18n="courses.edit_settings1") + if view.classroom + h3.modal-title(data-i18n="courses.edit_settings1") + else + h3.modal-title(data-i18n="courses.create_new_class") block modal-body-content .form .form-group 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 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 label(data-i18n="choose_hero.programming_language") 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="javascript", selected=aceConfig.language==='javascript') JavaScript block modal-footer-content - button#save-settings-btn.btn(data-i18n="common.save_changes") \ No newline at end of file + if view.classroom + button#save-settings-btn.btn(data-i18n="common.save_changes") + else + button#save-settings-btn.btn(data-i18n="courses.create_class") diff --git a/app/templates/courses/teacher-courses-view.jade b/app/templates/courses/teacher-courses-view.jade index ab59c8bb6..66eb4c5e0 100644 --- a/app/templates/courses/teacher-courses-view.jade +++ b/app/templates/courses/teacher-courses-view.jade @@ -19,7 +19,7 @@ block content .no-students No classes yet! .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 @@ -47,29 +47,29 @@ block footer mixin classroom(classroom) .row - - var classMemberCount = classroom.get('members').length + - var classMemberCount = classroom.get('members') ? classroom.get('members').length : 0; if classMemberCount > 0 .col-md-8 p span.spr.class-name= classroom.get('name') - if classroom.get('aceConfig') && classroom.get('aceConfig').language - span.spr (#{classroom.get('aceConfig').language}) + if classroom.get('aceConfig') && classroom.get('aceConfig').language === 'javascript' + span.spr.class-name (JavaScript) else 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 - - var courseInstances = view.courseInstances.where({classroomID: classroom.id}) + - var courseInstances = view.courseInstances.where({classroomID: classroom.id}); each courseInstance in courseInstances +course(courseInstance, classMemberCount) else .col-md-12 p span.spr.class-name= classroom.get('name') - if classroom.get('aceConfig') && classroom.get('aceConfig').language - span.spr (#{classroom.get('aceConfig').language}) + if classroom.get('aceConfig') && classroom.get('aceConfig').language === 'javascript' + span.spr.class-name (JavaScript) else 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! .text-center a.btn.btn-info.uppercase(href='/courses/#{classroom.id}') add students @@ -84,9 +84,9 @@ mixin classroom(classroom) .divider mixin course(courseInstance, classMemberCount) - - var courseMemberCount = courseInstance.get('members').length + - var courseMemberCount = courseInstance.get('members') ? courseInstance.get('members').length : 0; if courseMemberCount > 0 - - var course = view.courses.get(courseInstance.get('courseID')) + - var course = view.courses.get(courseInstance.get('courseID')); p .course-name= course.get('name') .course-enrolled #{courseMemberCount} / #{classMemberCount} students enrolled diff --git a/app/views/courses/ClassroomSettingsModal.coffee b/app/views/courses/ClassroomSettingsModal.coffee index bd4a5eb1b..e9b210e61 100644 --- a/app/views/courses/ClassroomSettingsModal.coffee +++ b/app/views/courses/ClassroomSettingsModal.coffee @@ -1,10 +1,11 @@ +Classroom = require 'models/Classroom' ModalView = require 'views/core/ModalView' template = require 'templates/courses/classroom-settings-modal' module.exports = class AddLevelSystemModal extends ModalView id: 'classroom-settings-modal' template: template - + events: 'click #save-settings-btn': 'onClickSaveSettingsButton' @@ -12,15 +13,16 @@ module.exports = class AddLevelSystemModal extends ModalView @classroom = options.classroom onClickSaveSettingsButton: -> - return unless @classroom - if name = $('.settings-name-input').val() + name = $('.settings-name-input').val() + unless @classroom + return unless name + @classroom = new Classroom({ name: name }) + if name @classroom.set('name', name) description = $('.settings-description-input').val() @classroom.set('description', description) @classroom.set('aceConfig', { language: @$('#programming-language-select').val() }) - @classroom.patch() + @classroom.save() @hide() - - \ No newline at end of file diff --git a/app/views/courses/TeacherCoursesView.coffee b/app/views/courses/TeacherCoursesView.coffee index 516c0d170..4c75e59ab 100644 --- a/app/views/courses/TeacherCoursesView.coffee +++ b/app/views/courses/TeacherCoursesView.coffee @@ -5,12 +5,9 @@ CocoModel = require 'models/CocoModel' Course = require 'models/Course' Classroom = require 'models/Classroom' User = require 'models/User' -Prepaid = require 'models/Prepaid' CourseInstance = require 'models/CourseInstance' RootView = require 'views/core/RootView' template = require 'templates/courses/teacher-courses-view' -utils = require 'core/utils' -InviteToClassroomModal = require 'views/courses/InviteToClassroomModal' ClassroomSettingsModal = require 'views/courses/ClassroomSettingsModal' module.exports = class TeacherCoursesView extends RootView @@ -18,11 +15,7 @@ module.exports = class TeacherCoursesView extends RootView template: template events: - 'click #create-new-class-btn': 'onClickCreateNewclassButton' - 'click .add-students-btn': 'onClickAddStudentsButton' - 'click .course-instance-membership-checkbox': 'onClickCourseInstanceMembershipCheckbox' - 'click #save-changes-btn': 'onClickSaveChangesButton' - 'click #manage-tab-link': 'onClickManageTabLink' + 'click .create-new-class': 'onClickCreateNewClassButton' 'click .edit-classroom-small': 'onClickEditClassroomSmall' 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') @supermodel.loadCollection(@courseInstances, 'course_instances', {data: {ownerID: me.id}}) @members = new CocoCollection([], { model: User }) - @prepaids = new CocoCollection([], { url: "/db/prepaid", model: Prepaid }) - 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') + @listenTo @members, 'sync', @render @ onceClassroomsSync: -> @@ -56,160 +41,19 @@ module.exports = class TeacherCoursesView extends RootView url: "/db/classroom/#{classroom.id}/members" }) - onClickCreateNewclassButton: -> - name = @$('#new-classroom-name-input').val() - return unless name - classroom = new Classroom({ name: name }) - classroom.save() - @classrooms.add(classroom) - classroom.saving = true - @renderManageTab() - @listenTo classroom, 'sync', -> - classroom.saving = false - @fillMissingCourseInstances() - - renderManageTab: -> - isActive = @$('#manage-tab-pane').hasClass('active') - @renderSelectors('#manage-tab-pane') - @$('#manage-tab-pane').toggleClass('active', isActive) + onClickCreateNewClassButton: -> + return @openModalView new AuthModal() 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', @render onClickEditClassroomSmall: (e) -> - classroomID = $(e.target).closest('small').data('classroom-id') + classroomID = $(e.target).data('classroom-id') classroom = @classrooms.get(classroomID) modal = new ClassroomSettingsModal({classroom: classroom}) @openModalView(modal) - @listenToOnce modal, 'hide', @renderManageTab - - 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') + @listenToOnce modal, 'hide', @render