diff --git a/README.md b/README.md index d6e11e265..0e451ff48 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ so we can accept your pull requests. It is easy. ![Josh Callebaut](https://dl.dropboxusercontent.com/u/138899/GitHub%20Wikis/avatars/Josh%20Callebaut/josh_callebaut_100.png "Josh Callebaut") ![Michael Schmatz](http://codecombat.com/images/pages/about/michael_small.png "Michael Schmatz") ![Josh Lee](http://codecombat.com/images/pages/about/josh_small.png "Josh Lee") +![Dan TDM](https://dl.dropboxusercontent.com/u/138899/GitHub%20Wikis/avatars/Dan_TDM/dan_tdm_100.png "Dan TDM") ![Alex Cotsarelis](https://dl.dropboxusercontent.com/u/138899/GitHub%20Wikis/avatars/Alex%20Cotsarelis/alex_100.png "Alex Cotsarelis") ![Alex Crooks](https://dl.dropboxusercontent.com/u/138899/GitHub%20Wikis/avatars/Alex%20Crooks/alex_100.png "Alex Crooks") ![Alexandru Caciulescu](https://dl.dropboxusercontent.com/u/138899/GitHub%20Wikis/avatars/Alexandru%20Caciulescu/alexandru_100.png "Alexandru Caciulescu") diff --git a/app/collections/LevelSessions.coffee b/app/collections/LevelSessions.coffee index dbbc343a5..20de80e42 100644 --- a/app/collections/LevelSessions.coffee +++ b/app/collections/LevelSessions.coffee @@ -5,12 +5,6 @@ module.exports = class LevelSessionCollection extends CocoCollection url: '/db/level.session' model: LevelSession - fetchMineForCourseInstance: (courseInstanceID, options) -> - options = _.extend({ - url: "/db/course_instance/#{courseInstanceID}/my-course-level-sessions" - }, options) - @fetch(options) - fetchForCourseInstance: (courseInstanceID, options) -> options = _.extend({ url: "/db/course_instance/#{courseInstanceID}/my-course-level-sessions" diff --git a/app/lib/LevelLoader.coffee b/app/lib/LevelLoader.coffee index 6a15df091..02b0b0753 100644 --- a/app/lib/LevelLoader.coffee +++ b/app/lib/LevelLoader.coffee @@ -488,7 +488,7 @@ module.exports = class LevelLoader extends CocoClass @world.difficulty = @session?.get('state')?.difficulty ? 0 if @observing @world.difficulty = Math.max 0, @world.difficulty - 1 # Show the difficulty they won, not the next one. - serializedLevel = @level.serialize(@supermodel, @session, @opponentSession) + serializedLevel = @level.serialize {@supermodel, @session, @opponentSession, @headless, @sessionless} @world.loadFromLevel serializedLevel, false console.log 'World has been initialized from level loader.' if LOG diff --git a/app/lib/simulator/Simulator.coffee b/app/lib/simulator/Simulator.coffee index 473ea06af..16e48dd6a 100644 --- a/app/lib/simulator/Simulator.coffee +++ b/app/lib/simulator/Simulator.coffee @@ -226,7 +226,7 @@ module.exports = class Simulator extends CocoClass @levelLoader = null setupGod: -> - @god.setLevel @level.serialize(@supermodel, @session, @otherSession) + @god.setLevel @level.serialize {@supermodel, @session, @otherSession, headless: true, sessionless: false} @god.setLevelSessionIDs (session.sessionID for session in @task.getSessions()) @god.setWorldClassMap @world.classMap @god.setGoalManager new GoalManager @world, @level.get('goals'), null, {headless: true} diff --git a/app/models/Level.coffee b/app/models/Level.coffee index ab02889d3..acad5b8a6 100644 --- a/app/models/Level.coffee +++ b/app/models/Level.coffee @@ -12,7 +12,8 @@ module.exports = class Level extends CocoModel urlRoot: '/db/level' editableByArtisans: true - serialize: (supermodel, session, otherSession, cached=false) -> + serialize: (options) -> + {supermodel, session, otherSession, @headless, @sessionless, cached=false} = options o = @denormalize supermodel, session, otherSession # hot spot to optimize # Figure out Components @@ -146,7 +147,7 @@ module.exports = class Level extends CocoModel levelThang.components.push placeholderComponent # Load the user's chosen hero AFTER getting stats from default char - if /Hero Placeholder/.test(levelThang.id) and @get('type', true) in ['course'] + if /Hero Placeholder/.test(levelThang.id) and @get('type', true) in ['course'] and not @headless and not @sessionless heroThangType = me.get('heroConfig')?.thangType or ThangType.heroes.captain levelThang.thangType = heroThangType if heroThangType diff --git a/app/schemas/models/classroom.schema.coffee b/app/schemas/models/classroom.schema.coffee index c06aebe2e..9f3e63ca7 100644 --- a/app/schemas/models/classroom.schema.coffee +++ b/app/schemas/models/classroom.schema.coffee @@ -25,6 +25,7 @@ _.extend ClassroomSchema.properties, levels: c.array { title: 'Levels' }, c.object { title: 'Level' }, { practice: {type: 'boolean'} practiceThresholdMinutes: {type: 'number'} + shareable: {type: 'boolean'} type: c.shortString() original: c.objectId() name: {type: 'string'} diff --git a/app/schemas/models/level.coffee b/app/schemas/models/level.coffee index 5d605948f..707bf6cd4 100644 --- a/app/schemas/models/level.coffee +++ b/app/schemas/models/level.coffee @@ -267,7 +267,7 @@ LevelSchema = c.object { victory: {} type: 'hero' goals: [ - {id: 'ogres-die', name: 'Ogres must die.', killThangs: ['ogres'], worldEndsAfter: 3} + {id: 'ogres-die', name: 'Defeat the ogres.', killThangs: ['ogres'], worldEndsAfter: 3} {id: 'humans-survive', name: 'Your hero must survive.', saveThangs: ['Hero Placeholder'], howMany: 1, worldEndsAfter: 3, hiddenGoal: true} ] concepts: ['basic_syntax'] @@ -325,6 +325,7 @@ _.extend LevelSchema.properties, replayable: {type: 'boolean', title: 'Replayable', description: 'Whether this (hero) level infinitely scales up its difficulty and can be beaten over and over for greater rewards.'} buildTime: {type: 'number', description: 'How long it has taken to build this level.'} practice: { type: 'boolean' } + shareable: { type: 'boolean', title: 'Shareable' } practiceThresholdMinutes: {type: 'number', description: 'Players with larger playtimes may be directed to a practice level.'} # Admin flags diff --git a/app/styles/modal/create-account-modal/basic-info-view.sass b/app/styles/modal/create-account-modal/basic-info-view.sass index a344ea89e..cf4a202d0 100644 --- a/app/styles/modal/create-account-modal/basic-info-view.sass +++ b/app/styles/modal/create-account-modal/basic-info-view.sass @@ -15,6 +15,16 @@ .form-group text-align: left + margin: 0 + + input + max-height: 5vh + + .form-container + > .form-group, > .row + max-height: 84px + flex-grow: 1 + align-self: flex-start .btn-illustrated img // Undo previous opacity-toggling hover behavior diff --git a/app/styles/modal/create-account-modal/choose-account-type-view.sass b/app/styles/modal/create-account-modal/choose-account-type-view.sass index 555f3970a..4e7a3408d 100644 --- a/app/styles/modal/create-account-modal/choose-account-type-view.sass +++ b/app/styles/modal/create-account-modal/choose-account-type-view.sass @@ -1,8 +1,16 @@ @import "app/styles/style-flat-variables" #choose-account-type-view + .choose-type-title + display: flex + flex-direction: column + flex-grow: 0.5 + justify-content: flex-end + + h4 + padding-bottom: 10px + .path-cards - margin-top: 15px display: flex .path-card ~ .path-card @@ -13,7 +21,8 @@ flex-direction: column justify-content: space-between width: 235px - min-height: 340px + height: 340px + max-height: 42vh border-style: solid border-width: thin border-radius: 5px @@ -35,6 +44,7 @@ align-items: center justify-content: center height: 50px + max-height: 5vh color: white font-weight: bold text-align: center @@ -43,7 +53,8 @@ flex-grow: 1 display: flex flex-direction: column - margin: 50px 20px 0 + justify-content: center + margin: 0 20px ul align-self: center @@ -55,14 +66,26 @@ left: -5px .card-footer - margin: 20px + margin: 0 20px 20px + min-height: 62px + display: flex + flex-direction: column + justify-content: flex-end .individual-section - margin-top: 50px + display: flex + flex-grow: 1 + flex-direction: column + align-items: center + justify-content: center max-width: 425px .individual-title font-weight: bold + + .individual-description + margin: 0 + flex-grow: 0.2 .text-h6 color: white diff --git a/app/styles/modal/create-account-modal/create-account-modal.sass b/app/styles/modal/create-account-modal/create-account-modal.sass index f40cb6f46..34607c9f4 100644 --- a/app/styles/modal/create-account-modal/create-account-modal.sass +++ b/app/styles/modal/create-account-modal/create-account-modal.sass @@ -17,6 +17,7 @@ display: flex flex-direction: column height: 850px + max-height: 90vh width: 850px text-align: center padding: 0 @@ -44,6 +45,7 @@ align-items: center justify-content: flex-end height: 100px + max-height: 10.5vh padding: 0 background-color: $navy diff --git a/app/templates/admin/analytics.jade b/app/templates/admin/analytics.jade index 51dc0c100..f5f7a1d4e 100644 --- a/app/templates/admin/analytics.jade +++ b/app/templates/admin/analytics.jade @@ -18,7 +18,7 @@ block content .col-md-5.big-stat.classroom-active-users div.description Classroom Monthly Active Users if view.activeUsers.length > 0 - - var classroomBigMAU = 0; + - var classroomBigMAU = 0; each count, event in view.activeUsers[0].events if event.indexOf('MAU classroom') >= 0 - classroomBigMAU += count; @@ -26,24 +26,24 @@ block content .col-md-5.big-stat.campaign-active-users div.description Campaign Monthly Active Users if view.activeUsers.length > 0 - - var campaignBigMAU = 0; + - var campaignBigMAU = 0; each count, event in view.activeUsers[0].events if event.indexOf('MAU campaign') >= 0 - campaignBigMAU += count; div.count= campaignBigMAU ul.nav.nav-tabs - li.active + li.active a(data-target="#tab_kpis", data-toggle="tab") KPIs - li + li a(data-target="#tab_active_classes", data-toggle="tab") Active Classes - li + li a(data-target="#tab_revenue", data-toggle="tab") Revenue - li + li a(data-target="#tab_classroom", data-toggle="tab") Classroom - li + li a(data-target="#tab_campaign", data-toggle="tab") Campaign - li + li a(data-target="#tab_campaign_vs_classroom", data-toggle="tab") Campaign vs Classroom .tab-content @@ -165,7 +165,7 @@ block content .small Paid teacher: at least one paid student in course instance .small Trial teacher: at least one trial student in course instance, and no paid students .small Free teacher: no paid students, no trial students - .small Paid status takes precedent over furthest course, so teacher furthest course is furthest course of highest paid status student + .small Paid status takes precedent over furthest course, so teacher furthest course is furthest course of highest paid status student if view.courseDistributionsRecent table.table.table-striped.table-condensed tr @@ -267,9 +267,9 @@ block content h1 Active Users if view.activeUsers.length > 0 - var eventNames = []; - each count, event in view.activeUsers[0].events + each event in view.activeUserEventNames if event.indexOf('classroom') >= 0 - - eventNames.push(event) + - eventNames.push(event); - eventNames.sort(function (a, b) {return a.localeCompare(b);}); table.table.table-striped.table-condensed tr @@ -293,7 +293,7 @@ block content each day in view.enrollmentDays tr td= day - if view.dayEnrollmentsMap[day] + if view.dayEnrollmentsMap[day] td= view.dayEnrollmentsMap[day].paidIssued || 0 td= view.dayEnrollmentsMap[day].paidRedeemed || 0 td= view.dayEnrollmentsMap[day].trialIssued || 0 @@ -322,9 +322,9 @@ block content h1 Active Users if view.activeUsers.length > 0 - var eventNames = []; - each count, event in view.activeUsers[0].events + each event in view.activeUserEventNames if event.indexOf('campaign') >= 0 - - eventNames.push(event) + - eventNames.push(event); - eventNames.sort(function (a, b) {return a.localeCompare(b);}); table.table.table-striped.table-condensed tr @@ -346,27 +346,13 @@ block content h1 Active Users if view.activeUsers.length > 0 - - var eventNames = []; - each count, event in view.activeUsers[0].events - - eventNames.push(event) - - eventNames.sort(function (a, b) { - - if (a.indexOf('campaign') == b.indexOf('campaign') || a.indexOf('classroom') == b.indexOf('classroom')) { - - return a.localeCompare(b); - - } - - else if (a.indexOf('campaign') > b.indexOf('campaign')) { - - return 1; - - } - - else { - - return -1; - - } - - }); table.table.table-striped.table-condensed tr th(style='min-width:85px;') Day - each eventName in eventNames + each eventName in view.activeUserEventNames th= eventName each activeUser in view.activeUsers tr td= activeUser.day - each eventName in eventNames + each eventName in view.activeUserEventNames td= activeUser.events[eventName] || 0 diff --git a/app/templates/core/create-account-modal/choose-account-type-view.jade b/app/templates/core/create-account-modal/choose-account-type-view.jade index af9917379..67a827854 100644 --- a/app/templates/core/create-account-modal/choose-account-type-view.jade +++ b/app/templates/core/create-account-modal/choose-account-type-view.jade @@ -1,5 +1,5 @@ .modal-body-content - h4 + h4.choose-type-title span(data-i18n="signup.choose_type") .path-cards .path-card.navy diff --git a/app/templates/core/create-account-modal/confirmation-view.jade b/app/templates/core/create-account-modal/confirmation-view.jade index 2ab37d834..cd38a0eaf 100644 --- a/app/templates/core/create-account-modal/confirmation-view.jade +++ b/app/templates/core/create-account-modal/confirmation-view.jade @@ -10,7 +10,7 @@ else p(data-i18n="signup.confirm_individual_blurb") - .signup-info-box-wrapper.m-y-3 + .signup-info-box-wrapper .text-burgandy(data-i18n="signup.write_this_down") .signup-info-box.text-center if me.get('name') @@ -32,4 +32,4 @@ span(data-i18n="general.email") | : #{me.get('email')} - button#start-btn.btn.btn-navy.btn-lg.m-y-3(data-i18n="signup.start_playing") + button#start-btn.btn.btn-navy.btn-lg.m-t-3(data-i18n="signup.start_playing") diff --git a/app/templates/core/create-account-modal/segment-check-view.jade b/app/templates/core/create-account-modal/segment-check-view.jade index 2311bdd36..e79b1eeee 100644 --- a/app/templates/core/create-account-modal/segment-check-view.jade +++ b/app/templates/core/create-account-modal/segment-check-view.jade @@ -45,7 +45,7 @@ form.modal-body.segment-check option(value='',data-i18n="calendar.day") for day in _.range(1,32) option(selected=(day == view.signupState.get('birthdayDay'))) #{day} - select#birthday-year-input.input-large.form-control(name="birthdayYear", style="width: 90px;") + select#birthday-year-input.input-large.form-control(name="birthdayYear", style="width: 90px; float: left") option(value='',data-i18n="calendar.year") - var thisYear = new Date().getFullYear() for year in _.range(thisYear, thisYear - 100, -1) diff --git a/app/templates/courses/teacher-class-view.jade b/app/templates/courses/teacher-class-view.jade index 8faf7e7a4..e6e025574 100644 --- a/app/templates/courses/teacher-class-view.jade +++ b/app/templates/courses/teacher-class-view.jade @@ -182,7 +182,8 @@ mixin studentsTab th.checkbox-col.select-all.small.text-center span(data-i18n="teacher.select_all") .checkbox-flat - input(type='checkbox' id='checkbox-all-students') + - var allStudentsChecked = _.all(state.get('checkboxStates')) + input(type='checkbox', id='checkbox-all-students', checked=allStudentsChecked) label.checkmark(for='checkbox-all-students') th +sortButtons @@ -201,7 +202,7 @@ 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) + input(type='checkbox' id='checkbox-student-' + student.id, data-student-id=student.id, checked=state.get('checkboxStates')[student.id]) label.checkmark(for='checkbox-student-' + student.id) td.student-info-col .student-info diff --git a/app/templates/editor/level/edit.jade b/app/templates/editor/level/edit.jade index 5496527d0..e64012f06 100644 --- a/app/templates/editor/level/edit.jade +++ b/app/templates/editor/level/edit.jade @@ -132,8 +132,9 @@ block outer_content div.tab-pane#editor-level-tasks-tab-view - div.tab-pane#editor-level-patches - .patches-view + div.tab-pane#editor-level-patches.nano + .nano-content + .patches-view div.tab-pane#related-achievements-view diff --git a/app/views/admin/AnalyticsView.coffee b/app/views/admin/AnalyticsView.coffee index f23c0e58a..c38e9ba28 100644 --- a/app/views/admin/AnalyticsView.coffee +++ b/app/views/admin/AnalyticsView.coffee @@ -73,6 +73,7 @@ module.exports = class AnalyticsView extends RootView # Add campaign/classroom DAU 30-day averages and daily totals campaignDauTotals = [] classroomDauTotals = [] + eventMap = {} for entry in @activeUsers day = entry.day campaignDauTotal = 0 @@ -82,18 +83,31 @@ module.exports = class AnalyticsView extends RootView campaignDauTotal += count else if event.indexOf('DAU classroom') >= 0 classroomDauTotal += count + eventMap[event] = true entry.events['DAU campaign total'] = campaignDauTotal + eventMap['DAU campaign total'] = true campaignDauTotals.unshift(campaignDauTotal) campaignDauTotals.pop() while campaignDauTotals.length > 30 if campaignDauTotals.length is 30 entry.events['DAU campaign 30-day average'] = Math.round(_.reduce(campaignDauTotals, (a, b) -> a + b) / 30) + eventMap['DAU campaign 30-day average'] = true entry.events['DAU classroom total'] = classroomDauTotal + eventMap['DAU classroom total'] = true classroomDauTotals.unshift(classroomDauTotal) classroomDauTotals.pop() while classroomDauTotals.length > 30 if classroomDauTotals.length is 30 entry.events['DAU classroom 30-day average'] = Math.round(_.reduce(classroomDauTotals, (a, b) -> a + b) / 30) + eventMap['DAU classroom 30-day average'] = true @activeUsers.sort (a, b) -> b.day.localeCompare(a.day) + @activeUserEventNames = Object.keys(eventMap) + @activeUserEventNames.sort (a, b) -> + if a.indexOf('campaign') is b.indexOf('campaign') or a.indexOf('classroom') is b.indexOf('classroom') + a.localeCompare(b) + else if a.indexOf('campaign') > b.indexOf('campaign') + 1 + else + -1 @updateAllKPIChartData() @updateActiveUsersChartData() @@ -134,13 +148,13 @@ module.exports = class AnalyticsView extends RootView return unless @revenue.length > 0 # Add monthly recurring revenue values - + # For each daily group, add up monthly values walking forward through time, and add to revenue groups monthlyDailyGroupMap = {} dailyGroupIndexMap = {} for group, i in @revenueGroups monthlyDailyGroupMap[group.replace('DRR', 'MRR')] = group - dailyGroupIndexMap[group] = i + dailyGroupIndexMap[group] = i for monthlyGroup, dailyGroup of monthlyDailyGroupMap monthlyValues = [] for i in [@revenue.length-1..0] @@ -183,7 +197,7 @@ module.exports = class AnalyticsView extends RootView @supermodel.addRequestResource({ url: '/db/prepaid/-/courses' method: 'POST' - data: {project: {maxRedeemers: 1, properties: 1, redeemers: 1}} + data: {project: {endDate: 1, maxRedeemers: 1, properties: 1, redeemers: 1}} success: (prepaids) => paidDayMaxMap = {} paidDayRedeemedMap = {} @@ -201,14 +215,13 @@ module.exports = class AnalyticsView extends RootView redeemDay = redeemer.date.substring(0, 10) trialDayRedeemedMap[redeemDay] ?= 0 trialDayRedeemedMap[redeemDay]++ - else + else if not prepaid.endDate? or new Date(prepaid.endDate) > new Date() paidDayMaxMap[day] ?= 0 paidDayMaxMap[day] += prepaid.maxRedeemers for redeemer in prepaid.redeemers redeemDay = redeemer.date.substring(0, 10) paidDayRedeemedMap[redeemDay] ?= 0 paidDayRedeemedMap[redeemDay]++ - @dayEnrollmentsMap = {} @paidCourseTotalEnrollments = [] for day, count of paidDayMaxMap @@ -218,7 +231,7 @@ module.exports = class AnalyticsView extends RootView @paidCourseTotalEnrollments.sort (a, b) -> a.day.localeCompare(b.day) @paidCourseRedeemedEnrollments = [] for day, count of paidDayRedeemedMap - @paidCourseRedeemedEnrollments.push({day: day, count: count}) + @paidCourseRedeemedEnrollments.push({day: day, count: count}) @dayEnrollmentsMap[day] ?= {paidIssued: 0, paidRedeemed: 0, trialIssued: 0, trialRedeemed: 0} @dayEnrollmentsMap[day].paidRedeemed += count @paidCourseRedeemedEnrollments.sort (a, b) -> a.day.localeCompare(b.day) @@ -239,7 +252,7 @@ module.exports = class AnalyticsView extends RootView }, 0).load() @courses = new CocoCollection([], { url: "/db/course", model: Course}) - @courses.comparator = "_id" + @courses.comparator = "_id" @listenToOnce @courses, 'sync', @onCoursesSync @supermodel.loadCollection(@courses) @@ -276,7 +289,7 @@ module.exports = class AnalyticsView extends RootView studentFurthestCourseMap = {} studentPaidStatusMap = {} for courseInstance in data.courseInstances - continue if utils.objectIdToDate(courseInstance._id) < startDate + continue if utils.objectIdToDate(courseInstance._id) < startDate courseID = courseInstance.courseID teacherID = courseInstance.ownerID for studentID in courseInstance.members @@ -306,7 +319,7 @@ module.exports = class AnalyticsView extends RootView # Paid teacher: at least one paid student # Trial teacher: at least one trial student in course instance, and no paid students # Free teacher: no paid students, no trial students - # Teacher furthest course is furthest course of highest paid status student + # Teacher furthest course is furthest course of highest paid status student teacherFurthestCourseMap = {} teacherPaidStatusMap = {} for teacher, students of teacherStudentsMap @@ -368,14 +381,17 @@ module.exports = class AnalyticsView extends RootView # Trim points preceding days if points.length and days.length and points[0].day.localeCompare(days[0]) < 0 - for point, i in points - if point.day.localeCompare(days[0]) >= 0 - points.splice(0, i) - break + if points[points.length - 1].day.localeCompare(days[0]) < 0 + points = [] + else + for point, i in points + if point.day.localeCompare(days[0]) >= 0 + points.splice(0, i) + break # Ensure points for each day for day, i in days - if points.length <= i or points[i].day isnt day + if points.length <= i or points[i]?.day isnt day prevY = if i > 0 then points[i - 1].y else 0.0 points.splice i, 0, day: day @@ -534,7 +550,7 @@ module.exports = class AnalyticsView extends RootView day = entry.day for event, count of entry.events eventDataMap[event] ?= [] - eventDataMap[event].push + eventDataMap[event].push day: entry.day value: count @@ -550,7 +566,7 @@ module.exports = class AnalyticsView extends RootView lines.push points: points description: event - lineColor: @lineColors[colorIndex++ % @lineColors.length] + lineColor: @lineColors[colorIndex++ % @lineColors.length] strokeWidth: 1 min: 0 showYScale: showYScale @@ -577,7 +593,7 @@ module.exports = class AnalyticsView extends RootView day = entry.day for event, count of entry.events eventDataMap[event] ?= [] - eventDataMap[event].push + eventDataMap[event].push day: entry.day value: count @@ -591,7 +607,7 @@ module.exports = class AnalyticsView extends RootView @campaignVsClassroomMonthlyActiveUsersRecentChartLines.push points: points description: event - lineColor: @lineColors[colorIndex++ % @lineColors.length] + lineColor: @lineColors[colorIndex++ % @lineColors.length] strokeWidth: 1 min: 0 showYScale: true @@ -601,7 +617,7 @@ module.exports = class AnalyticsView extends RootView @campaignVsClassroomMonthlyActiveUsersRecentChartLines.push points: points description: event - lineColor: @lineColors[colorIndex++ % @lineColors.length] + lineColor: @lineColors[colorIndex++ % @lineColors.length] strokeWidth: 1 min: 0 showYScale: false @@ -619,7 +635,7 @@ module.exports = class AnalyticsView extends RootView @campaignVsClassroomMonthlyActiveUsersChartLines.push points: points description: event - lineColor: @lineColors[colorIndex++ % @lineColors.length] + lineColor: @lineColors[colorIndex++ % @lineColors.length] strokeWidth: 1 min: 0 showYScale: true @@ -629,7 +645,7 @@ module.exports = class AnalyticsView extends RootView @campaignVsClassroomMonthlyActiveUsersChartLines.push points: points description: event - lineColor: @lineColors[colorIndex++ % @lineColors.length] + lineColor: @lineColors[colorIndex++ % @lineColors.length] strokeWidth: 1 min: 0 showYScale: false @@ -648,16 +664,14 @@ module.exports = class AnalyticsView extends RootView dailyMax = 0 data = [] - total = 0 for entry in @paidCourseTotalEnrollments - total += entry.count data.push day: entry.day - value: total + value: entry.count points = @createLineChartPoints(days, data) @enrollmentsChartLines.push points: points - description: 'Total paid enrollments issued' + description: 'Paid enrollments issued' lineColor: @lineColors[colorIndex++ % @lineColors.length] strokeWidth: 1 min: 0 @@ -666,16 +680,14 @@ module.exports = class AnalyticsView extends RootView dailyMax = _.max([dailyMax, _.max(points, 'y').y]) data = [] - total = 0 for entry in @paidCourseRedeemedEnrollments - total += entry.count data.push day: entry.day - value: total + value: entry.count points = @createLineChartPoints(days, data) @enrollmentsChartLines.push points: points - description: 'Total paid enrollments redeemed' + description: 'Paid enrollments redeemed' lineColor: @lineColors[colorIndex++ % @lineColors.length] strokeWidth: 1 min: 0 @@ -684,16 +696,14 @@ module.exports = class AnalyticsView extends RootView dailyMax = _.max([dailyMax, _.max(points, 'y').y]) data = [] - total = 0 for entry in @trialCourseTotalEnrollments - total += entry.count data.push day: entry.day - value: total - points = @createLineChartPoints(days, data) + value: entry.count + points = @createLineChartPoints(days, data, true) @enrollmentsChartLines.push points: points - description: 'Total trial enrollments issued' + description: 'Trial enrollments issued' lineColor: @lineColors[colorIndex++ % @lineColors.length] strokeWidth: 1 min: 0 @@ -702,16 +712,14 @@ module.exports = class AnalyticsView extends RootView dailyMax = _.max([dailyMax, _.max(points, 'y').y]) data = [] - total = 0 for entry in @trialCourseRedeemedEnrollments - total += entry.count data.push day: entry.day - value: total + value: entry.count points = @createLineChartPoints(days, data) @enrollmentsChartLines.push points: points - description: 'Total trial enrollments redeemed' + description: 'Trial enrollments redeemed' lineColor: @lineColors[colorIndex++ % @lineColors.length] strokeWidth: 1 min: 0 diff --git a/app/views/admin/MainAdminView.coffee b/app/views/admin/MainAdminView.coffee index 4a9d96145..dd4b33b32 100644 --- a/app/views/admin/MainAdminView.coffee +++ b/app/views/admin/MainAdminView.coffee @@ -23,6 +23,7 @@ module.exports = class MainAdminView extends RootView 'submit #user-search-form': 'onSubmitUserSearchForm' 'click #stop-spying-btn': 'onClickStopSpyingButton' 'click #increment-button': 'incrementUserAttribute' + 'click .user-spy-button': 'onClickUserSpyButton' 'click #user-search-result': 'onClickUserSearchResult' 'click #create-free-sub-btn': 'onClickFreeSubLink' 'click #terminal-create': 'onClickTerminalSubLink' @@ -31,31 +32,10 @@ module.exports = class MainAdminView extends RootView getTitle: -> return $.i18n.t('account_settings.admin') initialize: -> - @campaigns = new Campaigns() - @courses = new CocoCollection([], { url: "/db/course", model: Course}) - if window.amActually @amActually = new User({_id: window.amActually}) @amActually.fetch() @supermodel.trackModel(@amActually) - if me.isAdmin() - @supermodel.trackRequest @campaigns.fetchByType('course', { data: { project: 'levels' } }) - @supermodel.loadCollection(@courses, 'courses') - super() - - onLoaded: -> - campaignCourseIndexMap = {} - for course, index in @courses.models - campaignCourseIndexMap[course.get('campaignID')] = index + 1 - @courseLevels = [] - for campaign in @campaigns.models - continue unless campaignCourseIndexMap[campaign.id] - for levelID, level of campaign.get('levels') - @courseLevels.push({ - levelID - slug: level.slug - courseIndex: campaignCourseIndexMap[campaign.id] - }) super() onClickStopSpyingButton: -> @@ -80,6 +60,18 @@ module.exports = class MainAdminView extends RootView errors.showNotyNetworkError(arguments...) }) + onClickUserSpyButton: (e) -> + e.stopPropagation() + userID = $(e.target).closest('tr').data('user-id') + button = $(e.currentTarget) + forms.disableSubmit(button) + me.spy(userID, { + success: -> window.location.reload() + error: -> + forms.enableSubmit(button) + errors.showNotyNetworkError(arguments...) + }) + onSubmitUserSearchForm: (e) -> e.preventDefault() searchValue = @$el.find('#user-search').val() @@ -97,7 +89,7 @@ module.exports = class MainAdminView extends RootView forms.enableSubmit(@$('#user-search-button')) result = '' if users.length - result = ("
#{user._id}
#{user._id}