mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-03-14 07:00:01 -04:00
Add Course Progress Dots and Hovers
This commit is contained in:
parent
ecd82947ec
commit
d53f8eba39
3 changed files with 207 additions and 108 deletions
|
@ -1,3 +1,57 @@
|
|||
@import "app/styles/bootstrap/variables"
|
||||
@import "app/styles/mixins"
|
||||
@import "app/styles/style-flat-variables"
|
||||
|
||||
#teacher-student-view
|
||||
|
||||
|
||||
|
||||
.progress-dot
|
||||
margin: 5px
|
||||
|
||||
.progress-dot
|
||||
display: inline-block
|
||||
margin-right: 6px
|
||||
min-width: 34px
|
||||
height: 34px
|
||||
border-radius: 16px
|
||||
padding: 0 5px
|
||||
// margin-top: 23px
|
||||
// margin-bottom: 23px
|
||||
background: $gray-light
|
||||
position: relative
|
||||
a
|
||||
text-decoration: none
|
||||
|
||||
.dot-label
|
||||
padding-top: 2px
|
||||
.dot-label-inner
|
||||
font-size: 11px
|
||||
font-weight: bold
|
||||
color: white
|
||||
|
||||
.progress-dot-lg .dot-label .dot-label-inner
|
||||
font-size: 13px
|
||||
|
||||
.progress-dot.forest
|
||||
background: $forest
|
||||
.tooltip-inner
|
||||
color: $forest
|
||||
border-color: $forest
|
||||
.tooltip-arrow
|
||||
border-top-color: $forest
|
||||
|
||||
.progress-dot.gold
|
||||
background: $gold
|
||||
.tooltip-inner
|
||||
color: $navy
|
||||
border-color: $navy
|
||||
.tooltip-arrow
|
||||
border-top-color: $navy
|
||||
|
||||
.progress-dot.navy
|
||||
background: $navy
|
||||
.tooltip-inner
|
||||
color: $navy
|
||||
border-color: $navy
|
||||
.tooltip-arrow
|
||||
border-top-color: $navy
|
||||
|
|
|
@ -4,107 +4,125 @@ block page_nav
|
|||
include ../courses/teacher-dashboard-nav.jade
|
||||
|
||||
block content
|
||||
.container
|
||||
+breadcrumbs
|
||||
if !me.isAnonymous() && me.isTeacher()
|
||||
- var isOwner = view.classroom ? view.classroom.get('ownerID') === me.id : false;
|
||||
// - var validStudent = ...
|
||||
// load all students in this class
|
||||
// check if this student is in the class
|
||||
|
||||
if !view.user
|
||||
div Loading
|
||||
else
|
||||
.container
|
||||
+breadcrumbs
|
||||
if !me.isAnonymous() && me.isTeacher()
|
||||
- var isOwner = view.classroom ? view.classroom.get('ownerID') === me.id : false;
|
||||
// - var validStudent = ...
|
||||
// load all students in this class
|
||||
// check if this student is in the class
|
||||
|
||||
if isOwner
|
||||
// also check if validStudent
|
||||
h3 Student Profile:
|
||||
span= view.user.get('name')
|
||||
//- - console.log (view.user.get('email'))
|
||||
// this user object doesn't have an email property
|
||||
if (view.user.get('email'))
|
||||
//- - console.log ("User has an email")
|
||||
p Student Email:
|
||||
span= view.user.get('email')
|
||||
else
|
||||
p
|
||||
i Student has no email address set.
|
||||
p Last Level Played:
|
||||
span= view.lastPlayedString
|
||||
|
||||
- var status = view.user.prepaidStatus()
|
||||
span(data-i18n='view.user.status')
|
||||
span.spr License Status:
|
||||
strong(class= status === 'expired' ? 'text-danger' : '')= view.studentStatusString()
|
||||
|
||||
if isOwner
|
||||
// also check if validStudent
|
||||
h3 Student Profile:
|
||||
span= view.user.get('name')
|
||||
p Student Last Played:
|
||||
span= view.lastPlayedString
|
||||
|
||||
h4 Actions:
|
||||
// Assign courses from dropdown
|
||||
// Export CSV
|
||||
// Apply License
|
||||
// Edit password
|
||||
// Remove Student
|
||||
|
||||
|
||||
div Here's some course progress!
|
||||
|
||||
.student-levels
|
||||
div.student-levels-progress
|
||||
each versionedCourse in view.classroom.get('courses') || []
|
||||
- var course = _.find(view.courses.models, function(c) {return c.id === versionedCourse._id;});
|
||||
if !course
|
||||
- continue;
|
||||
- var instance = view.courseInstances.findWhere({ courseID: course.id, classroomID: view.classroom.id })
|
||||
- if (instance && instance.hasMember(view.user))
|
||||
div.course-info= course.get('name')
|
||||
- var levels = view.classroom.getLevels({courseID: course.id}).models
|
||||
each level, index in levels
|
||||
- var levelNumber = view.classroom.getLevelNumber(level.get('original'), index + 1)
|
||||
//- - console.log (level)
|
||||
- var levelProgress = view.levelProgressMap[level.get('original')]
|
||||
//- p= levelNumber + ". " + level.name + " Progress: " + view.levelProgressMap[level.original]
|
||||
//- - console.log(level)
|
||||
+studentLevelProgressDot(levelProgress, level, levelNumber, course)
|
||||
|
||||
|
||||
|
||||
if status == 'enrolled'
|
||||
div Student isn't in the following courses!
|
||||
each versionedCourse in view.classroom.get('courses') || []
|
||||
- var course = _.find(view.courses.models, function(c) {return c.id === versionedCourse._id;});
|
||||
if !course
|
||||
- continue;
|
||||
- var instance = view.courseInstances.findWhere({ courseID: course.id, classroomID: view.classroom.id })
|
||||
- if (!(instance && instance.hasMember(view.user)))
|
||||
div= course.get('name')
|
||||
// add a button to assign this course
|
||||
|
||||
//- else
|
||||
//- p Apply a license to this student to assign additional courses!
|
||||
//- button.enroll-student-button.btn.btn-navy(data-i18n="teacher.apply_license", data-user-id=view.user.id, data-event-action="Teachers Class Enrollment Enroll Student")
|
||||
//- // this button doesn't work yet
|
||||
|
||||
|
||||
- var status = view.user.prepaidStatus()
|
||||
span(data-i18n='view.user.status')
|
||||
span.spr License Status:
|
||||
strong(class= status === 'expired' ? 'text-danger' : '')= view.studentStatusString()
|
||||
|
||||
div Here's some course progress!
|
||||
|
||||
table.table
|
||||
each versionedCourse in view.classroom.get('courses') || []
|
||||
- var course = _.find(view.courses.models, function(c) {return c.id === versionedCourse._id;});
|
||||
|
||||
if !course
|
||||
- continue;
|
||||
- var instance = view.courseInstances.findWhere({ courseID: course.id, classroomID: view.classroom.id })
|
||||
- if (instance && instance.hasMember(view.user))
|
||||
th= course.get('name')
|
||||
each level, index in versionedCourse.levels || []
|
||||
- var levelNumber = view.classroom.getLevelNumber(level.original, index + 1)
|
||||
tr
|
||||
td= levelNumber
|
||||
td= level.name
|
||||
td= view.levelProgressMap[level.original]
|
||||
|
||||
if status == 'enrolled'
|
||||
div Student isn't in the following courses!
|
||||
each versionedCourse in view.classroom.get('courses') || []
|
||||
- var course = _.find(view.courses.models, function(c) {return c.id === versionedCourse._id;});
|
||||
if !course
|
||||
- continue;
|
||||
- var instance = view.courseInstances.findWhere({ courseID: course.id, classroomID: view.classroom.id })
|
||||
- if (!(instance && instance.hasMember(view.user)))
|
||||
div= course.get('name')
|
||||
// add a button to assign this course
|
||||
|
||||
mixin studentLevelProgressDot(levelProgress, level, levelNumber, course)
|
||||
//- TODO: Refactor with TeacherClassesView jade
|
||||
- dotClass = levelProgress == 'complete' ? 'forest' : (levelProgress == 'started' ? 'gold' : '');
|
||||
- levelName = level.get('name')
|
||||
- var context = { levelName: levelName, levelNumber: levelNumber, moment: moment , started: levelProgress == 'started', completed: levelProgress == 'complete'};
|
||||
if view.levelSessionMap && view.levelSessionMap[level.get('original')]
|
||||
- context.session = view.levelSessionMap[level.get('original')];
|
||||
- link = null;
|
||||
- labelText = levelNumber;
|
||||
if level.isLadder()
|
||||
- var courseInstance = view.courseInstances.findWhere({ courseID: course.id, classroomID: view.classroom.id });
|
||||
if courseInstance
|
||||
- link = view.urls.courseArenaLadder({level: level, courseInstance: courseInstance});
|
||||
- labelText = translate('courses.arena');
|
||||
else
|
||||
p Apply a license to this student to assign additional courses!
|
||||
button.enroll-student-button.btn.btn-navy(data-i18n="teacher.apply_license", data-user-id=view.user.id, data-event-action="Teachers Class Enrollment Enroll Student")
|
||||
// this button doesn't work yet
|
||||
|
||||
|
||||
//- - var paidFor = view.user.isEnrolled();
|
||||
//- for courseInstance in view.courseInstances.models
|
||||
//- - var inCourse = _.contains(courseInstance.get('members'), view.user.id);
|
||||
//- if !(inCourse || view.teacherMode)
|
||||
//- - continue;
|
||||
//- - var course = view.courses.get(courseInstance.get('courseID'));
|
||||
//- - var sessions = courseInstance.sessionsByUser[view.user.id] || [];
|
||||
//- if !(course.get('free') || paidFor)
|
||||
//- - continue;
|
||||
//- if inCourse
|
||||
//- .row
|
||||
//- .col-sm-3.text-right= i18n(course.attributes, 'name')
|
||||
//- .col-sm-9
|
||||
//- if inCourse
|
||||
//- - var levels = view.classroom.getLevels({courseID: course.id});
|
||||
//- - var numLevels = levels.size();
|
||||
//- - var sessionMap = _.zipObject(_.map(sessions, function(s) { return s.get('level').original; }), sessions);
|
||||
//- - var levelCellWidth = 100.00;
|
||||
//- if numLevels > 0
|
||||
//- - levelCellWidth = 100.00 / numLevels;
|
||||
//- - var css = "width:"+levelCellWidth+"%;"
|
||||
//- - var i = 0;
|
||||
//- .progress
|
||||
//- each trimModel in levels.models
|
||||
//- - var level = view.levels.get(trimModel.get('original')); // get the level loaded through the db
|
||||
//- if !level
|
||||
//- - continue;
|
||||
//- - var levelNumber = view.classroom.getLevelNumber(level.get('original'), i + 1)
|
||||
//- - i++
|
||||
//- - var session = sessionMap[level.get('original')];
|
||||
//- a(href=view.getLevelURL(level, course, courseInstance, session))
|
||||
//- - var content = view.levelPopoverContent(level, session, levelNumber);
|
||||
//- if session && session.get('state') && session.get('state').complete
|
||||
//- .progress-bar.progress-bar-complete(style=css, data-content=content, data-toggle='popover')= levelNumber
|
||||
//- else if session
|
||||
//- .progress-bar.progress-bar-started(style=css, data-content=content, data-toggle='popover')= levelNumber
|
||||
//- else
|
||||
//- .progress-bar.progress-bar-default(style=css, data-content=content, data-toggle='popover')= levelNumber
|
||||
//- else if paidFor
|
||||
//- .text-center
|
||||
//- button.enable-btn.btn.btn-info.btn-sm.text-uppercase(data-user-id=view.user.id, data-course-instance-cid=courseInstance.cid)
|
||||
//- span.spr(data-i18n="courses.assign")
|
||||
//- span= i18n(course.attributes, 'name')
|
||||
//-
|
||||
|
||||
- labelText = translate('courses.arena');
|
||||
- dotClass += ' progress-dot-lg';
|
||||
else if level.isProject()
|
||||
if levelProgress == 'started' || levelProgress == 'complete'
|
||||
if view.levelSessionMap && view.levelSessionMap[level.get('original')]
|
||||
- link = view.urls.playDevLevel({level: level, session: view.levelSessionMap[level.get('original')], course: course});
|
||||
- labelText = translate('teacher.view_student_project');
|
||||
- dotClass = 'navy';
|
||||
else
|
||||
- labelText = translate('teacher.project');
|
||||
- dotClass += ' progress-dot-lg';
|
||||
.progress-dot.level-progress-dot(class=dotClass, data-html='true', data-title=view.singleStudentLevelProgressDotTemplate(context))
|
||||
if link
|
||||
a(href=link)
|
||||
+progressDotLabel(labelText)
|
||||
else
|
||||
+progressDotLabel(labelText)
|
||||
|
||||
mixin progressDotLabel(label)
|
||||
.dot-label.text-center
|
||||
.dot-label-inner
|
||||
= label
|
||||
|
||||
mixin breadcrumbs
|
||||
.breadcrumbs
|
||||
a(data-i18n='teacher.my_classes' href='/teachers/classes')
|
||||
|
|
|
@ -23,11 +23,11 @@ module.exports = class TeacherStudentView extends RootView
|
|||
# helper: helper
|
||||
events:
|
||||
'click .assign-student-button': 'onClickAssignStudentButton'
|
||||
'click .enroll-student-button': 'onClickEnrollStudentButton'
|
||||
'click .enroll-student-button': 'onClickEnrollStudentButton' # this button isn't working yet
|
||||
|
||||
getTitle: -> return @user?.broadName()
|
||||
|
||||
initialize: (options, classroomID, studentID) ->
|
||||
initialize: (options, classroomID, @studentID) ->
|
||||
@classroom = new Classroom({_id: classroomID})
|
||||
@listenToOnce @classroom, 'sync', @onClassroomSync
|
||||
@supermodel.trackRequest(@classroom.fetch())
|
||||
|
@ -47,14 +47,35 @@ module.exports = class TeacherStudentView extends RootView
|
|||
# @levels.fetchForClassroom(classroomID, {data: {project: 'name,original,practice,slug'}})
|
||||
# @levels.on 'add', (model) -> @_byId[model.get('original')] = model # so you can 'get' them
|
||||
@supermodel.trackRequest(@levels.fetchForClassroom(classroomID, {data: {project: 'name,original'}}))
|
||||
#
|
||||
# @user = new User({_id: studentID})
|
||||
# @supermodel.trackRequest(@user.fetch())
|
||||
|
||||
@user = new User({_id: studentID})
|
||||
@supermodel.trackRequest(@user.fetch())
|
||||
@urls = require('core/urls')
|
||||
|
||||
|
||||
@singleStudentLevelProgressDotTemplate = require 'templates/teachers/hovers/progress-dot-single-student-level'
|
||||
@levelProgressMap = {}
|
||||
|
||||
super(options)
|
||||
|
||||
onLoaded: ->
|
||||
if @students.loaded and not @destroyed
|
||||
@user = _.find(@students.models, (s)=> s.id is @studentID)
|
||||
@updateLastPlayedString()
|
||||
@updateLevelProgressMap()
|
||||
@render()
|
||||
super()
|
||||
|
||||
afterRender: ->
|
||||
super(arguments...)
|
||||
$('.progress-dot, .btn-view-project-level').each (i, el) ->
|
||||
dot = $(el)
|
||||
dot.tooltip({
|
||||
html: true
|
||||
container: dot
|
||||
}).delegate '.tooltip', 'mousemove', ->
|
||||
dot.tooltip('hide')
|
||||
|
||||
onClassroomSync: ->
|
||||
# Now that we have the classroom from db, can request all level sessions for this classroom
|
||||
|
@ -63,6 +84,12 @@ module.exports = class TeacherStudentView extends RootView
|
|||
@listenTo @sessions, 'sync', @onSessionsSync
|
||||
@supermodel.trackRequests(@sessions.fetchForAllClassroomMembers(@classroom))
|
||||
|
||||
@students = new Users()
|
||||
jqxhrs = @students.fetchForClassroom(@classroom, removeDeleted: true)
|
||||
# @listenTo @students, ->
|
||||
# console.log @students
|
||||
@supermodel.trackRequests jqxhrs
|
||||
|
||||
onSessionsSync: ->
|
||||
# Now we have some level sessions, and enough data to calculate last played string
|
||||
# This may be called multiple times due to paged server API calls via fetchForAllClassroomMembers
|
||||
|
@ -97,7 +124,7 @@ module.exports = class TeacherStudentView extends RootView
|
|||
|
||||
updateLastPlayedString: ->
|
||||
# Make sure all our data is loaded, @sessions may not even be intialized yet
|
||||
return unless @courses.loaded and @levels.loaded and @sessions?.loaded and @user.loaded
|
||||
return unless @courses.loaded and @levels.loaded and @sessions?.loaded and @user?.loaded
|
||||
|
||||
# Use lodash to find the last session for our user, @sessions already sorted by changed date
|
||||
session = _.findLast @sessions.models, (s) => s.get('creator') is @user.id
|
||||
|
@ -128,18 +155,18 @@ module.exports = class TeacherStudentView extends RootView
|
|||
@render()
|
||||
|
||||
updateLevelProgressMap: ->
|
||||
return unless @courses.loaded and @levels.loaded and @sessions?.loaded and @user.loaded
|
||||
return unless @courses.loaded and @levels.loaded and @sessions?.loaded and @user?.loaded
|
||||
|
||||
# Map levels to sessions once, so we don't have to search entire session list multiple times below
|
||||
levelSessionMap = {}
|
||||
for session in @sessions.models
|
||||
levelSessionMap[session.get('level').original] = session
|
||||
@levelSessionMap = {}
|
||||
for session in @sessions.models when session.get('creator') is @studentID
|
||||
@levelSessionMap[session.get('level').original] = session
|
||||
|
||||
# Create mapping of level to student progress
|
||||
@levelProgressMap = {}
|
||||
for versionedCourse in @classroom.get('courses') ? []
|
||||
for versionedLevel in versionedCourse.levels
|
||||
session = levelSessionMap[versionedLevel.original]
|
||||
session = @levelSessionMap[versionedLevel.original]
|
||||
if session
|
||||
if session.get('state')?.complete
|
||||
@levelProgressMap[versionedLevel.original] = 'complete'
|
||||
|
|
Loading…
Reference in a new issue