Courses UI: Add course details condensed progress view
Defaulting to condensed view, with checkbox to expand details.
This commit is contained in:
parent
cf720ce270
commit
f966b8a2fc
3 changed files with 113 additions and 58 deletions
app
styles/courses/mock1
templates/courses/mock1
views/courses/mock1
|
@ -23,7 +23,6 @@
|
||||||
font-size: 9pt
|
font-size: 9pt
|
||||||
font-weight: normal
|
font-weight: normal
|
||||||
border: 1px solid gray
|
border: 1px solid gray
|
||||||
border-radius: 5px
|
|
||||||
margin: 0px
|
margin: 0px
|
||||||
padding: 2px
|
padding: 2px
|
||||||
background-color: white
|
background-color: white
|
||||||
|
@ -62,10 +61,10 @@
|
||||||
cursor: default
|
cursor: default
|
||||||
display: inline-block
|
display: inline-block
|
||||||
white-space: nowrap
|
white-space: nowrap
|
||||||
font-size: 9pt
|
font-size: 12px
|
||||||
|
line-height: 12px
|
||||||
font-weight: normal
|
font-weight: normal
|
||||||
border: 1px solid gray
|
border: 1px solid gray
|
||||||
border-radius: 5px
|
|
||||||
margin: 0px
|
margin: 0px
|
||||||
padding: 2px
|
padding: 2px
|
||||||
|
|
||||||
|
@ -83,7 +82,7 @@
|
||||||
font-size: 14px
|
font-size: 14px
|
||||||
|
|
||||||
.student-cell
|
.student-cell
|
||||||
min-width: 150px
|
width: 150px
|
||||||
|
|
||||||
.progress-cell
|
.progress-cell
|
||||||
padding: 2px
|
padding: 2px
|
||||||
|
@ -114,9 +113,9 @@
|
||||||
.progress-level-cell
|
.progress-level-cell
|
||||||
display: inline-block
|
display: inline-block
|
||||||
white-space: nowrap
|
white-space: nowrap
|
||||||
font-size: 9pt
|
font-size: 12px
|
||||||
|
line-height: 12px
|
||||||
border: 1px solid gray
|
border: 1px solid gray
|
||||||
border-radius: 5px
|
|
||||||
margin: 0px
|
margin: 0px
|
||||||
padding: 2px
|
padding: 2px
|
||||||
|
|
||||||
|
@ -131,9 +130,9 @@
|
||||||
.progress-concept-cell
|
.progress-concept-cell
|
||||||
display: inline-block
|
display: inline-block
|
||||||
white-space: nowrap
|
white-space: nowrap
|
||||||
font-size: 9pt
|
font-size: 12px
|
||||||
|
line-height: 12px
|
||||||
border: 1px solid gray
|
border: 1px solid gray
|
||||||
border-radius: 5px
|
|
||||||
margin: 0px
|
margin: 0px
|
||||||
padding: 2px
|
padding: 2px
|
||||||
|
|
||||||
|
@ -142,3 +141,6 @@
|
||||||
|
|
||||||
.progress-concept-cell-complete
|
.progress-concept-cell-complete
|
||||||
background-color: lightgray
|
background-color: lightgray
|
||||||
|
|
||||||
|
.condense-progress
|
||||||
|
width: 100%
|
||||||
|
|
|
@ -147,60 +147,107 @@ mixin progress-tab
|
||||||
span.progress-key.progress-key-complete complete
|
span.progress-key.progress-key-complete complete
|
||||||
span.progress-key.progress-key-started started
|
span.progress-key.progress-key-started started
|
||||||
span.progress-key not started
|
span.progress-key not started
|
||||||
if maxLastStartedIndex > 30
|
input.expand-progress-checkbox(type='checkbox')
|
||||||
input.expand-progress-checkbox(type='checkbox')
|
span.spl.expand-progress-label Expand details
|
||||||
span.spl.expand-progress-label(data-i18n="clans.exp_levels") Expand levels
|
|
||||||
tbody
|
tbody
|
||||||
each student in instance.students
|
each student in instance.students
|
||||||
tr
|
tr
|
||||||
td.student-cell
|
td.student-cell
|
||||||
a= student
|
a= student
|
||||||
- var levelsCompleted = 0
|
div #{stats[student].levelsCompleted} levels completed
|
||||||
each level in course.levels
|
div #{moment.duration(stats[student].secondsPlayed, 'seconds').humanize()} played
|
||||||
if userLevelStateMap[student][level] === 'complete'
|
div Played #{moment().subtract(stats[student].secondsLastPlayed, 'seconds').fromNow()}
|
||||||
- levelsCompleted++
|
|
||||||
- var secondsPlayed = Math.round(Math.random() * 1000 * (levelsCompleted + 1))
|
|
||||||
- var secondsLastPlayed = Math.round(Math.random() * 100000)
|
|
||||||
div #{levelsCompleted} levels completed
|
|
||||||
div #{moment.duration(secondsPlayed, 'seconds').humanize()} played
|
|
||||||
div Played #{moment().subtract(secondsLastPlayed, 'seconds').fromNow()}
|
|
||||||
td.progress-cell
|
td.progress-cell
|
||||||
.level-progression-concepts Concepts
|
if showExpandedProgress
|
||||||
each concept in courseConcepts
|
.level-progression-concepts Concepts
|
||||||
if userConceptsMap[student] && userConceptsMap[student][concept] === 'complete'
|
each concept in courseConcepts
|
||||||
span.spr.progress-concept-cell.progress-concept-cell-complete(data-i18n="concepts." + concept)
|
if userConceptsMap[student] && userConceptsMap[student][concept] === 'complete'
|
||||||
else if userConceptsMap[student] && userConceptsMap[student][concept] === 'started'
|
span.spr.progress-concept-cell.progress-concept-cell-complete(data-i18n="concepts." + concept)
|
||||||
span.spr.progress-concept-cell.progress-concept-cell-started(data-i18n="concepts." + concept)
|
else if userConceptsMap[student] && userConceptsMap[student][concept] === 'started'
|
||||||
else
|
span.spr.progress-concept-cell.progress-concept-cell-started(data-i18n="concepts." + concept)
|
||||||
span.spr.progress-concept-cell.progress-concept-cell-not-started(data-i18n="concepts." + concept)
|
else
|
||||||
|
span.spr.progress-concept-cell.progress-concept-cell-not-started(data-i18n="concepts." + concept)
|
||||||
|
|
||||||
.level-progression-levels Levels
|
.level-progression-levels Levels
|
||||||
- var i = 0
|
- var i = 0
|
||||||
each level in course.levels
|
each level in course.levels
|
||||||
if userLevelStateMap[student][level] === 'complete'
|
if userLevelStateMap[student][level] === 'complete'
|
||||||
span.progress-level-cell.progress-level-cell-complete #{i + 1}
|
span.progress-level-cell.progress-level-cell-complete #{i + 1}
|
||||||
if showExpandedProgress || i === 0 || i === course.levels.length - 1
|
|
||||||
span.spl= level.replace('Course: ', '')
|
span.spl= level.replace('Course: ', '')
|
||||||
.level-popup-container
|
.level-popup-container
|
||||||
h3 #{i + 1}. #{level.replace('Course: ', '')}
|
h3 #{i + 1}. #{level.replace('Course: ', '')}
|
||||||
p
|
p
|
||||||
div
|
|
||||||
- var playTime = Math.round(Math.random() * 600)
|
- var playTime = Math.round(Math.random() * 600)
|
||||||
span Time to solve
|
span Time to solve
|
||||||
span : #{moment.duration(playTime, "seconds").humanize()}
|
span : #{moment.duration(playTime, "seconds").humanize()}
|
||||||
div
|
p
|
||||||
- var completionDate = new Date()
|
- var completionDate = new Date()
|
||||||
- completionDate.setUTCDate(completionDate.getUTCDate() - Math.round(Math.random() * 60))
|
- completionDate.setUTCDate(completionDate.getUTCDate() - Math.round(Math.random() * 60))
|
||||||
span Completed on
|
span Completed on
|
||||||
span : #{moment(completionDate).format('MMMM Do YYYY, h:mm:ss a')}
|
span : #{moment(completionDate).format('MMMM Do YYYY, h:mm:ss a')}
|
||||||
strong(data-i18n="clans.view_solution") Click to view solution.
|
strong(data-i18n="clans.view_solution") Click to view solution.
|
||||||
else if userLevelStateMap[student][level] === 'started'
|
else if userLevelStateMap[student][level] === 'started'
|
||||||
span.progress-level-cell.progress-level-cell-started #{i + 1} #{level.replace('Course: ', '')}
|
span.progress-level-cell.progress-level-cell-started #{i + 1} #{level.replace('Course: ', '')}
|
||||||
else
|
.level-popup-container
|
||||||
span.progress-level-cell.level-progression-level-not-started #{i + 1}
|
h3 #{i + 1}. #{level.replace('Course: ', '')}
|
||||||
if showExpandedProgress || i === 0
|
p
|
||||||
span.spl= level.replace('Course: ', '')
|
- var completionDate = new Date()
|
||||||
- i++
|
- completionDate.setUTCDate(completionDate.getUTCDate() - Math.round(Math.random() * 60))
|
||||||
|
span Last played on
|
||||||
|
span : #{moment(completionDate).format('MMMM Do YYYY, h:mm:ss a')}
|
||||||
|
strong(data-i18n="clans.view_solution") Click to view solution.
|
||||||
|
else
|
||||||
|
span.progress-level-cell.level-progression-level-not-started #{i + 1} #{level.replace('Course: ', '')}
|
||||||
|
- i++
|
||||||
|
else
|
||||||
|
//- Condensed view
|
||||||
|
table
|
||||||
|
tbody
|
||||||
|
tr
|
||||||
|
td
|
||||||
|
.level-progression-concepts(style='margin:0px;') Concepts
|
||||||
|
td.condense-progress
|
||||||
|
each concept in courseConcepts
|
||||||
|
if userConceptsMap[student] && userConceptsMap[student][concept] === 'complete'
|
||||||
|
span.spr.progress-concept-cell.progress-concept-cell-complete(data-i18n="concepts." + concept)
|
||||||
|
else if userConceptsMap[student] && userConceptsMap[student][concept] === 'started'
|
||||||
|
span.spr.progress-concept-cell.progress-concept-cell-started(data-i18n="concepts." + concept)
|
||||||
|
else
|
||||||
|
break
|
||||||
|
tr
|
||||||
|
td
|
||||||
|
.level-progression-levels(style='margin:0px;') Levels
|
||||||
|
td.condense-progress
|
||||||
|
- var levelCellWidth = 100.00 / course.levels.length
|
||||||
|
- var i = 0
|
||||||
|
each level in course.levels
|
||||||
|
if userLevelStateMap[student][level] === 'complete'
|
||||||
|
span.progress-level-cell.progress-level-cell-complete(style="width:#{levelCellWidth}%;") #{i + 1}
|
||||||
|
.level-popup-container
|
||||||
|
h3 #{i + 1}. #{level.replace('Course: ', '')}
|
||||||
|
p
|
||||||
|
- var playTime = Math.round(Math.random() * 600)
|
||||||
|
span Time to solve
|
||||||
|
span : #{moment.duration(playTime, "seconds").humanize()}
|
||||||
|
p
|
||||||
|
- var completionDate = new Date()
|
||||||
|
- completionDate.setUTCDate(completionDate.getUTCDate() - Math.round(Math.random() * 60))
|
||||||
|
span Completed on
|
||||||
|
span : #{moment(completionDate).format('MMMM Do YYYY, h:mm:ss a')}
|
||||||
|
strong(data-i18n="clans.view_solution") Click to view solution.
|
||||||
|
else if userLevelStateMap[student][level] === 'started'
|
||||||
|
span.progress-level-cell.progress-level-cell-started(style="width:#{levelCellWidth}%;") #{i + 1}
|
||||||
|
.level-popup-container
|
||||||
|
h3 #{i + 1}. #{level.replace('Course: ', '')}
|
||||||
|
p
|
||||||
|
- var completionDate = new Date()
|
||||||
|
- completionDate.setUTCDate(completionDate.getUTCDate() - Math.round(Math.random() * 60))
|
||||||
|
span Last played on
|
||||||
|
span : #{moment(completionDate).format('MMMM Do YYYY, h:mm:ss a')}
|
||||||
|
strong(data-i18n="clans.view_solution") Click to view solution.
|
||||||
|
else
|
||||||
|
break
|
||||||
|
- i++
|
||||||
|
|
||||||
mixin levels-tab
|
mixin levels-tab
|
||||||
table.table.table-striped.table-condensed
|
table.table.table-striped.table-condensed
|
||||||
|
|
|
@ -37,7 +37,8 @@ module.exports = class CourseDetailsView extends RootView
|
||||||
context.memberSort = @memberSort
|
context.memberSort = @memberSort
|
||||||
context.userConceptsMap = @userConceptsMap ? {}
|
context.userConceptsMap = @userConceptsMap ? {}
|
||||||
context.userLevelStateMap = @userLevelStateMap ? {}
|
context.userLevelStateMap = @userLevelStateMap ? {}
|
||||||
context.showExpandedProgress = @course.levels.length <= 30 or @showExpandedProgress
|
context.showExpandedProgress = @showExpandedProgress
|
||||||
|
context.stats = @stats
|
||||||
context.studentMode = @options.studentMode ? false
|
context.studentMode = @options.studentMode ? false
|
||||||
|
|
||||||
conceptsCompleted = {}
|
conceptsCompleted = {}
|
||||||
|
@ -46,15 +47,6 @@ module.exports = class CourseDetailsView extends RootView
|
||||||
conceptsCompleted[concept] ?= 0
|
conceptsCompleted[concept] ?= 0
|
||||||
conceptsCompleted[concept]++
|
conceptsCompleted[concept]++
|
||||||
context.conceptsCompleted = conceptsCompleted
|
context.conceptsCompleted = conceptsCompleted
|
||||||
|
|
||||||
stats =
|
|
||||||
averageLevelPlaytime: _.random(30, 240)
|
|
||||||
averageLevelsCompleted: _.random(1, @course.levels.length)
|
|
||||||
stats.totalPlayTime = context.instance.students?.length * stats.averageLevelPlaytime ? 0
|
|
||||||
stats.totalLevelsCompleted = context.instance.students?.length * stats.averageLevelsCompleted ? 0
|
|
||||||
stats.lastLevelCompleted = @course.levels[@maxLastStartedIndex] ? @course.levels[@course.levels.length - 1]
|
|
||||||
context.stats = stats
|
|
||||||
|
|
||||||
context
|
context
|
||||||
|
|
||||||
initData: ->
|
initData: ->
|
||||||
|
@ -73,6 +65,10 @@ module.exports = class CourseDetailsView extends RootView
|
||||||
@levelMap = {}
|
@levelMap = {}
|
||||||
@levelMap[level] = true for level in @course.levels
|
@levelMap[level] = true for level in @course.levels
|
||||||
@userLevelStateMap = {}
|
@userLevelStateMap = {}
|
||||||
|
@stats =
|
||||||
|
averageLevelPlaytime: _.random(30, 240)
|
||||||
|
averageLevelsCompleted: _.random(1, @course.levels.length)
|
||||||
|
students: {}
|
||||||
@maxLastStartedIndex = -1
|
@maxLastStartedIndex = -1
|
||||||
for student in @instances?[@currentInstanceIndex].students
|
for student in @instances?[@currentInstanceIndex].students
|
||||||
@userLevelStateMap[student] = {}
|
@userLevelStateMap[student] = {}
|
||||||
|
@ -82,7 +78,17 @@ module.exports = class CourseDetailsView extends RootView
|
||||||
lastStartedIndex = lastCompletedIndex + 1
|
lastStartedIndex = lastCompletedIndex + 1
|
||||||
@userLevelStateMap[student][@course.levels[lastStartedIndex]] = 'started'
|
@userLevelStateMap[student][@course.levels[lastStartedIndex]] = 'started'
|
||||||
@maxLastStartedIndex = lastStartedIndex if lastStartedIndex > @maxLastStartedIndex
|
@maxLastStartedIndex = lastStartedIndex if lastStartedIndex > @maxLastStartedIndex
|
||||||
|
|
||||||
|
@stats[student] ?= {}
|
||||||
|
@stats[student].levelsCompleted = 0
|
||||||
|
@stats[student].levelsCompleted++ for level in @course.levels when @userLevelStateMap[student][level] is 'complete'
|
||||||
|
@stats[student].secondsPlayed = Math.round(Math.random() * 1000 * (@stats[student].levelsCompleted + 1))
|
||||||
|
@stats[student].secondsLastPlayed = Math.round(Math.random() * 100000)
|
||||||
@sortMembers()
|
@sortMembers()
|
||||||
|
@stats.totalPlayTime = @instances?[@currentInstanceIndex].students?.length * @stats.averageLevelPlaytime ? 0
|
||||||
|
@stats.totalLevelsCompleted = @instances?[@currentInstanceIndex].students?.length * @stats.averageLevelsCompleted ? 0
|
||||||
|
@stats.totalPlayTime = @instances?[@currentInstanceIndex].students?.length * @stats.averageLevelPlaytime ? 0
|
||||||
|
@stats.lastLevelCompleted = @course.levels[0] ? @course.levels[@course.levels.length - 1]
|
||||||
|
|
||||||
sortMembers: ->
|
sortMembers: ->
|
||||||
# Progress sort precedence: most completed concepts, most started concepts, most levels, name sort
|
# Progress sort precedence: most completed concepts, most started concepts, most levels, name sort
|
||||||
|
@ -188,7 +194,7 @@ module.exports = class CourseDetailsView extends RootView
|
||||||
container = $(e.target).find('.level-popup-container').show()
|
container = $(e.target).find('.level-popup-container').show()
|
||||||
margin = 20
|
margin = 20
|
||||||
offset = $(e.target).offset()
|
offset = $(e.target).offset()
|
||||||
scrollTop = $(e.target).offsetParent().scrollTop()
|
scrollTop = $('#page-container').scrollTop()
|
||||||
height = container.outerHeight()
|
height = container.outerHeight()
|
||||||
container.css('left', offset.left + e.offsetX)
|
container.css('left', offset.left + e.offsetX)
|
||||||
container.css('top', offset.top + scrollTop - height - margin)
|
container.css('top', offset.top + scrollTop - height - margin)
|
||||||
|
|
Reference in a new issue