mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-12-01 03:16:56 -05:00
891d0fe12f
This was a race condition where the view would trigger a render before courses loaded, and the template required them when it had some of its other resources.
431 lines
17 KiB
Text
431 lines
17 KiB
Text
extends /templates/base-flat
|
|
|
|
block page_nav
|
|
include ./teacher-dashboard-nav.jade
|
|
|
|
block content
|
|
- var classroom = view.classroom
|
|
if !me.isTeacher()
|
|
.alert.alert-danger.text-center
|
|
.container
|
|
// DNT: Temporary
|
|
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
|
|
|
|
if classroom.loaded
|
|
.container
|
|
+breadcrumbs
|
|
if classroom.get('archived')
|
|
.row.center-block.text-center.m-t-3.m-b-3
|
|
.unarchive-btn.btn.btn-lg.btn-navy
|
|
span(data-i18n='teacher.unarchive_this_class')
|
|
|
|
h3.m-t-2= classroom.get('name')
|
|
a.label.edit-classroom(data-classroom-id=classroom.id)
|
|
span(data-i18n='teacher.edit_class_settings')
|
|
h4= classroom.get('description')
|
|
|
|
.classroom-info-row.row.m-t-5
|
|
.classroom-details.col-md-3
|
|
- var stats = state.get('classStats')
|
|
h4.m-b-2(data-i18n='teacher.class_overview')
|
|
|
|
.language.small-details
|
|
span(data-i18n='teacher.language')
|
|
span.spr :
|
|
span= classroom.capitalLanguage
|
|
|
|
.student-count.small-details
|
|
span(data-i18n='teacher.students')
|
|
span.spr :
|
|
span= classroom.get('members').length
|
|
|
|
.average-playtime.small-details
|
|
span(data-i18n='teacher.avg_playtime')
|
|
span.spr :
|
|
span= stats.averagePlaytime
|
|
|
|
.total-playtime.small-details
|
|
span(data-i18n='teacher.total_playtime')
|
|
span.spr :
|
|
span= stats.totalPlaytime
|
|
|
|
.average-complete.small-details
|
|
span(data-i18n='teacher.avg_completed')
|
|
span.spr :
|
|
span= stats.averageLevelsComplete
|
|
|
|
.total-complete.small-details
|
|
span(data-i18n='teacher.total_completed')
|
|
span.spr :
|
|
span= stats.totalLevelsComplete
|
|
|
|
.total-complete.small-details
|
|
span(data-i18n='teacher.created')
|
|
span.spr :
|
|
span= moment(classroom.created()).format('l')
|
|
|
|
if view.students && view.students.models.length > 0
|
|
button.export-student-progress-btn.btn.btn-lg.btn-primary
|
|
span(data-i18n='teacher.export_student_progress')
|
|
|
|
//- .concepts.small-details
|
|
//- if state.get('progressData')
|
|
//- div
|
|
//- span(data-i18n='teacher.concepts_covered')
|
|
//- span :
|
|
//- - console.log('concepts', view.conceptData)
|
|
//- - concepts = view.conceptData
|
|
//- each state, name in view.conceptData[view.classroom.id]
|
|
//- if state.get('started')
|
|
//- b.concept(class=state.get('completed') ? 'forest' : 'gold')
|
|
//- span(data-i18n='concepts.'+name)
|
|
|
|
.completeness-info.col-md-4
|
|
h4.m-b-2
|
|
|
|
if state.get('earliestIncompleteLevel')
|
|
div.small-details
|
|
span(data-i18n='teacher.earliest_incomplete')
|
|
span :
|
|
+longLevelName(state.get('earliestIncompleteLevel'))
|
|
+inlineUserList(state.get('earliestIncompleteLevel').users)
|
|
|
|
if state.get('latestCompleteLevel')
|
|
div.small-details.m-t-3
|
|
span(data-i18n='teacher.latest_complete')
|
|
span :
|
|
+longLevelName(state.get('latestCompleteLevel'))
|
|
+inlineUserList(state.get('latestCompleteLevel').users)
|
|
|
|
.adding-students.col-md-5
|
|
h4.m-b-2
|
|
span(data-i18n='teacher.adding_students')
|
|
span :
|
|
+copyCodes
|
|
+addStudentsButton
|
|
|
|
if view.students.length > 0
|
|
ul.nav.nav-tabs.m-t-5(role='tablist')
|
|
- var activeTab = state.get('activeTab');
|
|
li(class=(activeTab === "#students-tab" ? 'active' : ''))
|
|
a.students-tab-btn(href='#students-tab')
|
|
.small-details.text-center(data-i18n='teacher.students')
|
|
.tab-spacer
|
|
li(class=(activeTab === "#course-progress-tab" ? 'active' : ''))
|
|
a.course-progress-tab-btn(href='#course-progress-tab')
|
|
.small-details.text-center(data-i18n='teacher.course_progress')
|
|
.tab-spacer
|
|
li(class=(activeTab === "#enrollment-status-tab" ? 'active' : ''))
|
|
a.course-progress-tab-btn(href='#enrollment-status-tab')
|
|
.small-details.text-center(data-i18n='teacher.enrollment_status')
|
|
.tab-filler
|
|
|
|
.tab-content
|
|
if activeTab === '#students-tab'
|
|
+studentsTab
|
|
else if activeTab === '#course-progress-tab'
|
|
+courseProgressTab
|
|
else
|
|
+enrollmentStatusTab
|
|
|
|
else
|
|
.text-center.m-t-5.m-b-5
|
|
.text-h2
|
|
span(data-i18n="teacher.no_students_yet")
|
|
.text-h4
|
|
span(data-i18n="teacher.try_refreshing")
|
|
|
|
mixin breadcrumbs
|
|
.breadcrumbs
|
|
a(data-i18n='teacher.my_classes' href='/teachers/classes')
|
|
span.spl.spr >
|
|
//- TODO: Use .glyphicon-menu-right when we update bootstrap
|
|
span
|
|
= view.classroom.get('name')
|
|
|
|
mixin longLevelName(data)
|
|
if data
|
|
div.level-name
|
|
span.spr Course
|
|
span= data.courseNumber
|
|
span.spr , Level
|
|
span= data.levelNumber
|
|
span.spr :
|
|
span= data.levelName
|
|
else
|
|
div.level-name(data-i18n='teacher.not_applicable')
|
|
|
|
mixin inlineUserList(users)
|
|
if users
|
|
ul.inline-student-list.small
|
|
each student in users
|
|
li
|
|
//- a(href='TODO')
|
|
//- = student.broadName()
|
|
span.inline-student-name
|
|
= student.broadName()
|
|
|
|
mixin addStudentsButton
|
|
.add-students.text-center
|
|
a.add-students-btn.btn.btn-lg.btn-primary(data-classroom-id=view.classroom.id)
|
|
span(data-i18n='teacher.add_students_manually')
|
|
|
|
mixin studentsTab
|
|
#students-tab
|
|
+bulkAssignControls
|
|
table.students-table
|
|
thead
|
|
th.checkbox-col.select-all.small.text-center
|
|
span(data-i18n="teacher.select_all")
|
|
.checkbox-flat
|
|
input(type='checkbox' id='checkbox-all-students')
|
|
label.checkmark(for='checkbox-all-students')
|
|
th
|
|
+sortButtons
|
|
tbody
|
|
each student in state.get('students').models
|
|
+studentRow(student)
|
|
|
|
mixin sortButtons
|
|
.sort-buttons.small
|
|
span(data-i18n='teacher.sort_by')
|
|
span.spr :
|
|
button.sort-button.sort-by-name(data-i18n='general.name', value='name')
|
|
button.sort-button.sort-by-progress(data-i18n='teacher.progress', value='progress')
|
|
|
|
mixin studentRow(student)
|
|
tr.student-row.alternating-background
|
|
td.checkbox-col.student-checkbox
|
|
.checkbox-flat
|
|
input(type='checkbox' id='checkbox-student-' + student.id, data-student-id=student.id)
|
|
label.checkmark(for='checkbox-student-' + student.id)
|
|
td.student-info-col
|
|
.student-info
|
|
if student.get('deleted')
|
|
em (deleted)
|
|
div.student-name= student.broadName()
|
|
div.student-email.small-details= student.get('email')
|
|
td.hidden
|
|
a.edit-student-button(data-student-id=student.id)
|
|
span.glyphicon.glyphicon-edit
|
|
span(data-i18n='teacher.edit')
|
|
td.latest-level-col.small
|
|
div
|
|
i
|
|
span(data-i18n='teacher.latest_completed')
|
|
span :
|
|
div
|
|
+longLevelName(student.latestCompleteLevel)
|
|
td
|
|
if state.get('progressData')
|
|
each trimCourse, index in view.classroom.get('courses')
|
|
- var course = view.courses.get(trimCourse._id);
|
|
- var instance = view.courseInstances.findWhere({ courseID: course.id, classroomID: classroom.id })
|
|
if instance && instance.hasMember(student)
|
|
- var progress = state.get('progressData').get({ classroom: view.classroom, course: course, user: student })
|
|
- var levelsTotal = trimCourse.levels.length
|
|
//- - var level = ???
|
|
+studentCourseProgressDot(progress, levelsTotal, level, 'CS' + (index+1))
|
|
unless student.isEnrolled()
|
|
+enrollStudentButton(student)
|
|
//- td
|
|
//- span.view-class-arrow.glyphicon.glyphicon-chevron-right
|
|
td
|
|
.pull-right
|
|
a.edit-student-link.small.center-block.text-center.m-r-2(data-student-id=student.id)
|
|
div.glyphicon.glyphicon-edit
|
|
div(data-i18n='teacher.edit')
|
|
a.remove-student-link.small.center-block.text-center.m-r-2(data-student-id=student.id)
|
|
div.glyphicon.glyphicon-remove
|
|
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)
|
|
span(data-i18n='teacher.enroll_student')
|
|
|
|
mixin courseProgressTab
|
|
#course-progress-tab.m-t-3
|
|
if view.courses
|
|
.text-center
|
|
span(data-i18n='teacher.select_course')
|
|
span.spr :
|
|
select.course-select
|
|
each trimCourse in view.classroom.get('courses')
|
|
- var course = view.courses.get(trimCourse._id);
|
|
option(value=course.id selected=(course===state.get('selectedCourse')))
|
|
= course.get('name')
|
|
if state.get('progressData')
|
|
.render-on-course-sync
|
|
+courseOverview
|
|
.student-levels-table
|
|
+sortButtons
|
|
each student in state.get('students').models
|
|
if _.contains(state.get('selectedCourse').members, student.id)
|
|
+studentLevelsRow(student)
|
|
//- TODO: If any students aren't assigned the course
|
|
.unassigned-students.render-on-course-sync
|
|
if state.get('selectedCourse') && state.get('selectedCourse').members.length < state.get('students').length
|
|
h2
|
|
span(data-i18n='TODO')
|
|
| Students who have not been assigned
|
|
|
|
|
span= state.get('selectedCourse').get('name')
|
|
for student in state.get('students').models
|
|
unless _.contains(state.get('selectedCourse').members, student.id)
|
|
.row.unassigned-student-row.alternating-background
|
|
.student-name.col-sm-3
|
|
= student.broadName()
|
|
.student-email.small-details.col-sm-3
|
|
= student.get('email')
|
|
.col-sm-4
|
|
.latest-completed.truncate.small
|
|
i.m-r-1
|
|
span(data-i18n='TODO')
|
|
| Latest completed
|
|
| :
|
|
+longLevelName(student.latestCompleteLevel)
|
|
.col-sm-2
|
|
if student.isEnrolled()
|
|
.assign-student-button.btn.btn-md.btn-navy.pull-right(data-user-id=student.id data-course-id=state.get('selectedCourse').id)
|
|
span(data-i18n='TODO')
|
|
| Assign Course
|
|
else
|
|
.enroll-student-button.btn.btn-md.btn-navy.pull-right(data-user-id=student.id)
|
|
span(data-i18n='TODO')
|
|
| Enroll Student
|
|
|
|
mixin courseOverview
|
|
- var course = state.get('selectedCourse')
|
|
- var levels = view.classroom.getLevels({courseID: course.id, withoutLadderLevels: true}).models
|
|
.course-overview-row
|
|
.course-title.student-name
|
|
span= course.get('name')
|
|
span :
|
|
span(data-i18n='teacher.course_overview')
|
|
.course-overview-progress
|
|
each level, index in levels
|
|
- var progress = state.get('progressData').get({ classroom: view.classroom, course: course, level: level })
|
|
+allStudentsLevelProgressDot(progress, level, index+1)
|
|
|
|
mixin studentLevelsRow(student)
|
|
.student-levels-row.alternating-background
|
|
div.student-info
|
|
div.student-name= student.broadName()
|
|
div.student-email.small-details= student.get('email')
|
|
div.student-levels-progress
|
|
- var course = state.get('selectedCourse')
|
|
- var levels = view.classroom.getLevels({courseID: course.id, withoutLadderLevels: true}).models
|
|
each level, index in levels
|
|
- var progress = state.get('progressData').get({ classroom: view.classroom, course: course, level: level, user: student })
|
|
+studentLevelProgressDot(progress, level, index+1, session)
|
|
|
|
mixin studentCourseProgressDot(progress, levelsTotal, level, label)
|
|
//- TODO: Refactor with TeacherClassesView jade
|
|
//- TODO: Give classes abbreviations instead of using index?
|
|
- dotClass = progress.completed ? 'forest' : (progress.started ? 'gold' : '');
|
|
- _.assign(progress, { levelsTotal: levelsTotal })
|
|
.progress-dot(class=dotClass, data-html='true', data-title=view.singleStudentCourseProgressDotTemplate(progress))
|
|
+progressDotLabel(label)
|
|
|
|
mixin allStudentsLevelProgressDot(progress, level, levelNumber)
|
|
- dotClass = progress.completed ? 'forest' : (progress.started ? 'gold' : '');
|
|
- levelName = level.get('name')
|
|
- context = _.merge(progress, { levelName: levelName, levelNumber: levelNumber, numStudents: view.students.length })
|
|
.progress-dot.level-progress-dot(class=dotClass, data-html='true', data-title=view.allStudentsLevelProgressDotTemplate(context))
|
|
+progressDotLabel(levelNumber)
|
|
|
|
mixin studentLevelProgressDot(progress, level, levelNumber)
|
|
//- TODO: Refactor with TeacherClassesView jade
|
|
- dotClass = progress.completed ? 'forest' : (progress.started ? 'gold' : '');
|
|
- levelName = level.get('name')
|
|
- context = _.merge(progress, { levelName: levelName, levelNumber: levelNumber, moment: moment })
|
|
.progress-dot.level-progress-dot(class=dotClass, data-html='true', data-title=view.singleStudentLevelProgressDotTemplate(context))
|
|
+progressDotLabel(levelNumber)
|
|
|
|
mixin progressDotLabel(label)
|
|
.dot-label.text-center
|
|
.dot-label-inner
|
|
= label
|
|
|
|
mixin copyCodes
|
|
div.copy-button-group.form-inline.m-b-3
|
|
.form-group
|
|
input.text-h4.semibold#join-code-input(value=state.get('classCode'))
|
|
button#copy-code-btn.form-control.btn.btn-lg.btn-forest
|
|
span(data-i18n='teacher.copy_class_code')
|
|
div.text-center.small(data-i18n='teacher.class_code_blurb')
|
|
|
|
div.copy-button-group.form-inline.m-b-3
|
|
.form-group
|
|
input.form-control.text-h4.semibold#join-url-input(value=state.get('joinURL'))
|
|
button#copy-url-btn.form-control.btn.btn-lg.btn-forest
|
|
span(data-i18n='teacher.copy_class_url')
|
|
div.text-center.small(data-i18n='teacher.class_join_url_blurb')
|
|
|
|
mixin bulkAssignControls
|
|
.bulk-assign-controls.form-inline
|
|
- console.log('state errors', state.get('errors'))
|
|
.no-students-selected.small-details(class=state.get('errors').assigningToNobody ? 'visible' : '')
|
|
span(data-i18n='teacher.no_students_selected')
|
|
.cant-assign-to-unenrolled.small-details(class=state.get('errors').assigningToUnenrolled ? 'visible' : '')
|
|
span(data-i18n='teacher.cant_assign_to_unenrolled')
|
|
span.small
|
|
span(data-i18n='teacher.bulk_assign')
|
|
span :
|
|
select.bulk-course-select.form-control
|
|
each trimCourse in _.rest(view.classroom.get('courses'))
|
|
- var course = view.courses.get(trimCourse._id)
|
|
- console.log('???', course)
|
|
option(value=course.id selected=(course===state.get('selectedCourse')))
|
|
= course.get('name')
|
|
button.btn.btn-primary-alt.assign-to-selected-students
|
|
span(data-i18n='teacher.assign_to_selected_students')
|
|
button.btn.btn-primary-alt.enroll-selected-students
|
|
span(data-i18n='teacher.enroll_selected_students')
|
|
|
|
mixin enrollmentStatusTab
|
|
// TODO: Have search input in all tabs
|
|
|
|
//form.form-inline.text-center.m-t-3
|
|
// #search-form-group.form-group
|
|
// label(for="student-search") Search for student:
|
|
// input#student-search.form-control.m-l-1(type="search")
|
|
// span.glyphicon.glyphicon-search.form-control-feedback
|
|
|
|
table.table#enrollment-status-table.table-condensed.m-t-3
|
|
thead
|
|
// Checkbox code works, but don't need it yet.
|
|
//th.checkbox-col.select-all
|
|
.checkbox-flat
|
|
input(type='checkbox' id='checkbox-all-students')
|
|
label.checkmark(for='checkbox-all-students')
|
|
th
|
|
.sort-buttons.small
|
|
span(data-i18n='teacher.sort_by')
|
|
span.spr :
|
|
button.sort-button.sort-by-name(data-i18n='general.name', value='name')
|
|
button.sort-button.sort-by-status(data-i18n='user.status', value='status')
|
|
tbody
|
|
- var searchTerm = view.state.get('searchTerm');
|
|
each student in state.get('students').search(searchTerm)
|
|
- var status = student.prepaidStatus()
|
|
tr.student-row.alternating-background
|
|
//td.checkbox-col.student-checkbox
|
|
.checkbox-flat
|
|
input(type='checkbox' id='checkbox-student-' + student.id, data-student-id=student.id)
|
|
label.checkmark(for='checkbox-student-' + student.id)
|
|
td.student-info-col
|
|
.student-info
|
|
div.student-name= student.broadName()
|
|
div.student-email.small-details= student.get('email')
|
|
td.status-col
|
|
span(data-i18n='user.status')
|
|
span.spr :
|
|
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)
|