Merge branch 'master' into production

This commit is contained in:
Matt Lott 2015-07-02 20:04:27 -07:00
commit ff953dbd5a
14 changed files with 397 additions and 166 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 192 KiB

View file

@ -790,6 +790,7 @@
make_private: "Make clan private"
subs_only: "subscribers only"
create_clan: "Create New Clan"
private_preview: "Preview"
public_clans: "Public Clans"
my_clans: "My Clans"
clan_name: "Clan Name"

View file

@ -158,7 +158,7 @@ module.exports = nativeDescription: "Español (América Latina)", englishDescrip
unwatch: "No seguir"
submit_patch: "Enviar Parche"
submit_changes: "Enviar cambios"
# save_changes: "Save Changes"
save_changes: "Guardar cambios"
general:
and: "y"
@ -609,7 +609,7 @@ module.exports = nativeDescription: "Español (América Latina)", englishDescrip
sub_includes_4: "Soporte de correo electronico Premium"
sub_includes_5: "7 heroes nuevos con habilidades unicas que dominar"
sub_includes_6: "bonificación de 3500 gemas cada mes"
# sub_includes_7: "Private Clans"
sub_includes_7: "Clanes privados"
# monitor_progress_title: "How do I monitor student progress?"
# monitor_progress_1: "Student progress can be monitored by creating a"
# monitor_progress_2: "for your class."
@ -635,39 +635,39 @@ module.exports = nativeDescription: "Español (América Latina)", englishDescrip
# how_much_6: "for more details."
# more_info_title: "Where can I find more information?"
# more_info_1: "Our"
# more_info_2: "teachers forum"
# more_info_3: "is a good place to connect with fellow educators who are using CodeCombat."
more_info_2: "el foro de profesores"
more_info_3: "es un buen lugar para connectarse con los educadores que estan usando CodeCombat."
sys_requirements_title: "Requerimientos del sistema"
sys_requirements_1: "Debido que CodeCombat es un juego, es más difícil para las computadoras correrlo en relación a un tutorial escrito o un video. Para que todos puedan jugar, hemos optimizado la web para correr rápidamente en todos los navegadores modernos y en maquinas antiguas. Dicho esto, aquí están nuestras sugerencias para sacar el máximo provecho de su experiencia en la Hora del Código:" # {change}
sys_requirements_2: "Usar una versión actualizada del navegador Chrome o Firefox." # {change}
# teachers_survey:
teachers_survey:
# title: "Teacher Survey"
# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above."
# retrieving: "Retrieving information..."
# being_reviewed_1: "Your application for a free trial subscription is being"
# being_reviewed_2: "reviewed."
# approved_1: "Your application for a free trial subscription was"
# approved_2: "approved."
approved_2: "Aprobada."
# approved_3: "Further instructions have been sent to"
# denied_1: "Your application for a free trial subscription has been"
# denied_2: "denied."
# contact_1: "Please contact"
denied_2: "denegadae."
contact_1: "Porfavor contactarse"
# contact_2: "if you have further questions."
# description_1: "We offer free subscriptions to teachers for evaluation purposes. You can find more information on our"
# description_2: "teachers"
# description_3: "page."
# description_4: "Please fill out this quick survey and well email you setup instructions."
# email: "Email Address"
# school: "Name of School"
# location: "Name of City"
# age_students: "How old are your students?"
email: "Dirección de email"
school: "Nombre del colegio"
location: "Nombre de la ciudad"
age_students: "¿Qué edad tienen tus estudiantes?"
# under: "Under"
# other: "Other:"
# amount_students: "How many students do you teach?"
# hear_about: "How did you hear about CodeCombat?"
# fill_fields: "Please fill out all fields."
# thanks: "Thanks! We'll send you setup instructions shortly."
amount_students: "¿A cuantos alumnos les enseñas?"
hear_about: "¿Donde escuchaste sobre CodeCombat?"
fill_fields: "Porfavor llenar todos los campos."
thanks: "Gracias! Vamos a mandarte instrucciónes para iniciar proximamente."
versions:
save_version_title: "Guardar nueva versión"
@ -705,7 +705,7 @@ module.exports = nativeDescription: "Español (América Latina)", englishDescrip
picture_tab: "Imagen"
delete_account_tab: "Borra tu cuenta"
wrong_email: "Mail Incorrecto"
# wrong_password: "Wrong Password"
wrong_password: "Contraseña incorrecta"
upload_picture: "Sube una imagen"
delete_this_account: "Borrar esta cuenta permanentemente"
god_mode: "Modo Dios"
@ -746,7 +746,7 @@ module.exports = nativeDescription: "Español (América Latina)", englishDescrip
keyboard_shortcuts: "Atajos de teclado"
space: "Barra espaciadora"
enter: "Enter"
# press_enter: "press enter"
press_enter: "Toca enter"
escape: "Escape"
shift: "Shift"
run_code: "Ejecutar el código."
@ -781,48 +781,48 @@ module.exports = nativeDescription: "Español (América Latina)", englishDescrip
social_hipchat: "Chatea con nosotros en el chat público de CodeCombat en la sala HipChat"
contribute_to_the_project: "Contribuir al proyecto"
# clans:
# clan: "Clan"
# clans: "Clans"
# new_name: "New clan name"
# new_description: "New clan description"
# make_private: "Make clan private"
# subs_only: "subscribers only"
# create_clan: "Create New Clan"
# public_clans: "Public Clans"
# my_clans: "My Clans"
# clan_name: "Clan Name"
# name: "Name"
clans:
clan: "Clan"
clans: "Clanes"
new_name: "Nuevo nombre de clan"
new_description: "descripción del clan"
make_private: "Hacer clan privado"
subs_only: "solo suscriptores"
create_clan: "Crear nuevo clan"
public_clans: "Clanes publicos"
my_clans: "Mis Clanes"
clan_name: "Nombre del clan"
name: "Nombre"
# chieftain: "Chieftain"
# type: "Type"
# edit_clan_name: "Edit Clan Name"
# edit_clan_description: "Edit Clan Description"
# edit_name: "edit name"
# edit_description: "edit description"
# private: "(private)"
type: "Tipo"
edit_clan_name: "Editar el nombre del Clan"
edit_clan_description: "Editar descripción del clan"
edit_name: "editar nombre"
edit_description: "editar descripción"
private: "(privado)"
# summary: "Summary"
# average_level: "Average Level"
# average_achievements: "Average Achievements"
# delete_clan: "Delete Clan"
# leave_clan: "Leave Clan"
# join_clan: "Join Clan"
# invite_1: "Invite:"
# invite_2: "*Invite players to this Clan by sending them this link."
# members: "Members"
# progress: "Progress"
# not_started_1: "not started"
# started_1: "started"
# complete_1: "complete"
# exp_levels: "Expand levels"
# rem_hero: "Remove Hero"
# status: "Status"
# complete_2: "Complete"
# started_2: "Started"
# not_started_2: "Not Started"
# view_solution: "Click to view solution."
# latest_achievement: "Latest Achievement"
# playtime: "Playtime"
# last_played: "Last played"
average_level: "Nivel Promedio"
average_achievements: "Logros Promedio"
delete_clan: "Borrar Clan"
leave_clan: "Abandonar Clan"
join_clan: "Ingresar Clan"
invite_1: "Invitar:"
invite_2: "*Invitar jugadores al clan, mandandoles este link."
members: "Miembros"
progress: "Progreso"
not_started_1: "no iniciado"
started_1: "iniciado"
complete_1: "completo"
exp_levels: "Expand levels"
rem_hero: "Remover Heróe"
status: "Stado"
complete_2: "Completo"
started_2: "Iniciado"
not_started_2: "No inciiado"
view_solution: "Click para ver la solución."
latest_achievement: "último logro"
playtime: "Tiempo de juego"
last_played: "Último jugado"
classes:
archmage_title: "Archimago"
@ -1074,7 +1074,7 @@ module.exports = nativeDescription: "Español (América Latina)", englishDescrip
no_achievements: "Sin Logros todavía."
favorite_prefix: "Idioma favorito "
favorite_postfix: "."
# not_member_of_clans: "Not a member of any clans yet."
not_member_of_clans: "No se es miembro de ningún clan todavía."
achievements:
last_earned: "Último Ganado"
@ -1180,7 +1180,7 @@ module.exports = nativeDescription: "Español (América Latina)", englishDescrip
user_remarks: "Observaciones del usuario"
versions: "Versiones"
items: "Items"
# hero: "Hero"
hero: "Heróe"
heroes: "Héroes"
achievement: "Logros"
clas: "CLAs"

View file

@ -641,7 +641,7 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese",
sys_requirements_1: "モダンなブラウザ。最新の Chrome や FireFox, Safari など。Internet Explorer 9 以上。"
sys_requirements_2: "CodeCombat はまだ iPad をサポートしていません。"
# teachers_survey:
teachers_survey:
# title: "Teacher Survey"
# must_be_logged: "You must be logged in first. Please create an account or log in from the menu above."
# retrieving: "Retrieving information..."
@ -659,8 +659,8 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese",
# description_3: "page."
# description_4: "Please fill out this quick survey and well email you setup instructions."
# email: "Email Address"
# school: "Name of School"
# location: "Name of City"
school: "学校名"
location: "市町村"
# age_students: "How old are your students?"
# under: "Under"
# other: "Other:"
@ -705,7 +705,7 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese",
picture_tab: "画像"
delete_account_tab: "アカウントの削除"
wrong_email: "間違ったメールアドレス"
# wrong_password: "Wrong Password"
wrong_password: "間違ったパスワード"
upload_picture: "画像をアップロード"
delete_this_account: "アカウントを完全削除する"
god_mode: "ゴッドモード"
@ -714,8 +714,8 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese",
admin: "管理者"
new_password: "新パスワード"
new_password_verify: "新パスワードを再入力"
type_in_email: "削除を確認するため、メールを入力して下さい" # {change}
# type_in_password: "Also, type in your password."
type_in_email: "アカウントの削除を確認するため、メールアドレスを入力して下さい"
type_in_password: "そして、パスワードを入力してください。"
email_subscriptions: "ニュースレターの購読"
email_subscriptions_none: "No Email Subscriptions."
email_announcements: "お知らせ"
@ -746,7 +746,7 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese",
keyboard_shortcuts: "キーボードショートカット"
space: "スペース"
enter: "エンター"
# press_enter: "press enter"
press_enter: "エンターを押す"
escape: "エスケープ"
shift: "シフト"
run_code: "現在のコードを実行"

View file

@ -55,8 +55,11 @@
vertical-align: middle
.member-header
cursor: pointer
.progress-header
margin-right: 14px
cursor: pointer
.progress-key
cursor: default
@ -74,6 +77,7 @@
.progress-key-complete
background-color: lightgray
margin-left: 14px
.expand-progress-checkbox
margin-left: 14px
@ -95,9 +99,23 @@
background-color: blanchedalmond
font-size: 10pt
.level-progression-campaign
.level-progression-concepts
color: #317EAC
font-size: 12pt
font-weight: bold
margin-top: 8px
margin-bottom: 4px
.level-progression-levels
color: #317EAC
font-size: 12pt
font-weight: bold
margin-top: 8px
.level-progression-campaign
font-size: 10pt
font-weight: bold
margin-bottom: 4px
margin-top: 4px
.progress-level-cell

View file

@ -10,3 +10,60 @@
.select-session
width: 300px
display: inline
.progress-header
margin-right: 14px
.progress-key
cursor: default
display: inline-block
white-space: nowrap
font-size: 9pt
font-weight: normal
border: 1px solid gray
border-radius: 5px
margin: 0px
padding: 2px
.progress-key-started
background-color: lightgreen
.progress-key-complete
background-color: lightgray
.expand-progress-checkbox
margin-left: 14px
.expand-progress-label
font-weight: normal
font-size: 14px
.progress-cell
padding: 2px
padding-bottom: 10px
.level-popup-container
display: none
position: absolute
padding: 10px
border: 1px solid black
z-index: 3
background-color: blanchedalmond
font-size: 10pt
.progress-level-cell
display: inline-block
white-space: nowrap
font-size: 9pt
border: 1px solid gray
border-radius: 5px
margin: 0px
padding: 2px
.progress-level-cell-started
cursor: pointer
background-color: lightgreen
.progress-level-cell-complete
cursor: pointer
background-color: lightgray

View file

@ -83,92 +83,111 @@ block content
table.table.table-condensed
thead
tr
th(data-i18n="resources.hero") Hero
th
span.progress-header(data-i18n="clans.progress") Progress
span.progress-key(data-i18n="clans.not_started_1") not started
span.member-header.spr(data-i18n="resources.hero") Hero
if memberSort === 'nameAsc'
span.member-header.glyphicon.glyphicon-chevron-up
else if memberSort === 'nameDesc'
span.member-header.glyphicon.glyphicon-chevron-down
th
span.progress-header.spr(data-i18n="clans.progress") Progress
if memberSort === 'progressAsc'
span.progress-header.glyphicon.glyphicon-chevron-up
else if memberSort === 'progressDesc'
span.progress-header.glyphicon.glyphicon-chevron-down
else
span(style='padding-left:16px;')
span.spl.progress-key.progress-key-complete(data-i18n="clans.complete_1") complete
span.progress-key.progress-key-started(data-i18n="clans.started_1") started
span.progress-key.progress-key-complete(data-i18n="clans.complete_1") complete
span.progress-key(data-i18n="clans.not_started_1") not started
input.expand-progress-checkbox(type='checkbox')
span.spl.expand-progress-label(data-i18n="clans.exp_levels") Expand levels
tbody
each member in members
tr
td
div
span.hero-icon-cell
span.spr.player-hero-icon(data-memberid="#{member.id}")
span.code-language-cell
if memberLanguageMap && memberLanguageMap[member.id]
span.code-language-cell(style="background-image: url(/images/common/code_languages/#{memberLanguageMap[member.id]}_small.png)", title=memberLanguageMap[member.id])
div
a(href="/user/#{member.id}")= member.get('name') || 'Anoner'
div Level #{member.level()}
if isOwner && member.id !== clan.get('ownerID')
button.btn.btn-xs.btn-warning.remove-member-btn(data-id="#{member.id}", data-i18n="clans.rem_hero") Remove Hero
td.progress-cell
td
div
span.hero-icon-cell
span.spr.player-hero-icon(data-memberid="#{member.id}")
span.code-language-cell
if memberLanguageMap && memberLanguageMap[member.id]
span.code-language-cell(style="background-image: url(/images/common/code_languages/#{memberLanguageMap[member.id]}_small.png)", title=memberLanguageMap[member.id])
div
a(href="/user/#{member.id}")= member.get('name') || 'Anoner'
div Level #{member.level()}
if isOwner && member.id !== clan.get('ownerID')
button.btn.btn-xs.btn-warning.remove-member-btn(data-id="#{member.id}", data-i18n="clans.rem_hero") Remove Hero
td.progress-cell
.level-progression-concepts Concepts
each concept in conceptsProgression
if userConceptsMap[member.id] && userConceptsMap[member.id][concept] === 'complete'
span.spr.progress-level-cell.progress-level-cell-complete(data-i18n="concepts." + concept)
else if userConceptsMap[member.id] && userConceptsMap[member.id][concept] === 'started'
span.spr.progress-level-cell.progress-level-cell-started(data-i18n="concepts." + concept)
else
span.spr.progress-level-cell.progress-level-cell-not-started(data-i18n="concepts." + concept)
.level-progression-levels Levels
each campaign in campaignLevelProgressions
if lastUserCampaignLevelMap[member.id] && lastUserCampaignLevelMap[member.id][campaign.ID]
div.level-progression-campaign= campaign.name
- var i = 0
each campaign in campaignLevelProgressions
if lastUserCampaignLevelMap[member.id] && lastUserCampaignLevelMap[member.id][campaign.ID]
div.level-progression-campaign= campaign.name
- var i = 0
each level in campaign.levels
- i++
- var state = null, levelInfo = null
if memberLevelStateMap[member.id][level.slug]
- levelInfo = memberLevelStateMap[member.id][level.slug].levelInfo
- state = memberLevelStateMap[member.id][level.slug].state
if state === 'complete'
span.progress-level-cell.progress-level-cell-complete(data-level-info=levelInfo) #{i}
if showExpandedProgress || i === 1 || i === lastUserCampaignLevelMap[member.id][campaign.ID].index + 1
span.spl #{level.name}
.level-popup-container
h3 #{i}. #{levelInfo.level}
p
each level in campaign.levels
- i++
- var state = null, levelInfo = null
if memberLevelStateMap[member.id][level.slug]
- levelInfo = memberLevelStateMap[member.id][level.slug].levelInfo
- state = memberLevelStateMap[member.id][level.slug].state
if state === 'complete'
span.progress-level-cell.progress-level-cell-complete(data-level-info=levelInfo) #{i}
if showExpandedProgress || i === 1 || i === lastUserCampaignLevelMap[member.id][campaign.ID].index + 1
span.spl #{level.name}
.level-popup-container
h3 #{i}. #{levelInfo.level}
p
div
span(data-i18n="clans.status") Status
span.spr :
span(data-i18n="clans.complete_2") Complete
div
span(data-i18n="clans.playtime") Playtime
span : #{levelInfo.playtime}s
div
span(data-i18n="clans.last_played") Last played
span : #{levelInfo.changed}
if isOwner || me.isAdmin()
strong(data-i18n="clans.view_solution") Click to view solution.
else if state === 'started'
span.progress-level-cell.progress-level-cell-started(data-level-info=levelInfo) #{i}
if showExpandedProgress || i === 1 || i === lastUserCampaignLevelMap[member.id][campaign.ID].index + 1
span.spl #{level.name}
.level-popup-container
h3 #{i}. #{level.name}
p
div
span(data-i18n="clans.status") Status
span.spr :
span(data-i18n="clans.started_2") Started
div
span(data-i18n="clans.playtime") Playtime
span : #{levelInfo.playtime}s
div
span(data-i18n="clans.last_played") Last played
span : #{levelInfo.changed}
if isOwner || me.isAdmin()
strong(data-i18n="clans.view_solution") Click to view solution.
else
span.progress-level-cell.level-progression-level-not-started #{i}
if showExpandedProgress || i === 1 || i === lastUserCampaignLevelMap[member.id][campaign.ID].index + 1
span.spl #{level.name}
.level-popup-container
h3 #{i}. #{level.name}
div
span(data-i18n="choose_hero.status") Status
span.spr :
span(data-i18n="clans.complete_2") Complete
div
span(data-i18n="clans.playtime") Playtime
span : #{levelInfo.playtime}s
div
span(data-i18n="clans.last_played") Last played
span : #{levelInfo.changed}
if isOwner || me.isAdmin()
strong(data-i18n="clans.view_solution") Click to view solution.
else if state === 'started'
span.progress-level-cell.progress-level-cell-started(data-level-info=levelInfo) #{i}
if showExpandedProgress || i === 1 || i === lastUserCampaignLevelMap[member.id][campaign.ID].index + 1
span.spl #{level.name}
.level-popup-container
h3 #{i}. #{level.name}
p
div
span(data-i18n="choose_hero.status") Status
span.spr :
span(data-i18n="clans.started_2") Started
div
span(data-i18n="clans.playtime") Playtime
span : #{levelInfo.playtime}s
div
span(data-i18n="clans.last_played") Last played
span : #{levelInfo.changed}
if isOwner || me.isAdmin()
strong(data-i18n="clans.view_solution") Click to view solution.
else
span.progress-level-cell.level-progression-level-not-started #{i}
if showExpandedProgress || i === 1 || i === lastUserCampaignLevelMap[member.id][campaign.ID].index + 1
span.spl #{level.name}
.level-popup-container
h3 #{i}. #{level.name}
div
span(data-i18n="choose_hero.status") Status
span.spr :
span(data-i18n="clans.not_started_2") Not Started
if lastUserCampaignLevelMap[member.id][campaign.ID].levelSlug === level.slug
- break
span(data-i18n="clans.status") Status
span.spr :
span(data-i18n="clans.not_started_2") Not Started
if lastUserCampaignLevelMap[member.id][campaign.ID].levelSlug === level.slug
- break
//- Basic dashboard
else

View file

@ -10,7 +10,7 @@ block content
input(type='checkbox').private-clan-checkbox
span.spl(data-i18n="clans.make_private") Make clan private
span.spl (
a.private-more-info(data-i18n="clans.subs_only") subscribers only
a.private-more-info(data-i18n="clans.private_preview")
span )
p
button.btn.btn-success.create-clan-btn(data-i18n="clans.create_clan") Create New Clan

View file

@ -11,9 +11,11 @@ block content
input.student-view-checkbox(type='checkbox')
span.spl Student view
div TODO: fix ugly tabs
div TODO: add student progress monitoring
div TODO: level concepts, status, working play button
div TODO: student view
div TODO: aggregate student progress
div TODO: student level progress popups
div TODO: student concept progress
div(style='border-bottom: 1px solid black;')
h1= course.title
@ -64,24 +66,48 @@ block content
table.table.table-condensed
thead
tr
th Name
th Progress
th
th
span.progress-header Progress
span.progress-key.progress-key-complete complete
span.progress-key.progress-key-started started
span.progress-key not started
if maxLastStartedIndex > 30
input.expand-progress-checkbox(type='checkbox')
span.spl.expand-progress-label(data-i18n="clans.exp_levels") Expand levels
tbody
each student in instance.students
tr
td
a= student
td TODO: level progress
td
a= student
td.progress-cell
- var i = 0
each level in course.levels
if i <= userLevelStateMap[student].lastCompletedIndex
span.progress-level-cell.progress-level-cell-complete #{i + 1}
if showExpandedProgress || i === 0 || i === course.levels.length - 1
span.spl #{level}
else if i <= userLevelStateMap[student].lastStartedIndex
span.progress-level-cell.progress-level-cell-started #{i + 1}
if showExpandedProgress || i === 1 || i === userLevelStateMap[student].lastStartedIndex
span.spl #{level}
else
span.progress-level-cell.level-progression-level-not-started #{i + 1}
if showExpandedProgress || i === 1 || i === userLevelStateMap[student].lastStartedIndex
span.spl #{level}
if i === maxLastStartedIndex
- break
- i++
.tab-pane#invite(role='tabpanel')
p Invite students to join this class.
if course.title !== 'Introduction to Computer Science'
p Student unlock code: #{instance.code}
p Class capacity: 34/50
textarea.textarea-emails(rows=3, placeholder="Enter student emails to invite, one per line")
div
button.btn.btn-success.btn-invite Send Invites
.tab-pane#levels(role='tabpanel')
table.table.table-condensed
thead

View file

@ -48,11 +48,25 @@ block content
span.spl(data-i18n="teachers.monitor_progress_4")
p(data-i18n="teachers.monitor_progress_5")
h4(data-i18n="teachers.sub_includes_7")
p(data-i18n="teachers.private_clans_1")
ul
li
strong Track concepts
span.spl learned by each student
li Track levels completed for each student
li
span See your students'
strong.spl solutions
li Sort students by name or progress
li
strong Requires invitation
span.spl to join
p
img(src='/images/pages/clans/dashboard_preview.png' height='400')
p
span.spr(data-i18n="teachers.private_clans_2")
a(href='/clans', data-i18n="clans.clan")
span(data-i18n="teachers.private_clans_3")
p Private clans require a subscription to create or join.
h3(data-i18n="teachers.material_title")
if me.get('chinaVersion')
@ -78,19 +92,19 @@ block content
tbody
tr
td Syntax
td If/Else
td If Statements
td Arithmetic
td Object Literals
tr
td Methods
td Relational Operators
td While-loops
td While Loops
td Remote Method Invocation
tr
td Parameters
td Object Properties
td Break
td For-Loops
td Break Statements
td For Loops
tr
td Strings
td Input Handling

View file

@ -27,6 +27,8 @@ module.exports = class ClanDetailsView extends RootView
'click .edit-name-save-btn': 'onEditNameSave'
'click .join-clan-btn': 'onJoinClan'
'click .leave-clan-btn': 'onLeaveClan'
'click .member-header': 'onClickMemberHeader'
'click .progress-header': 'onClickProgressHeader'
'click .progress-level-cell': 'onClickLevel'
'click .remove-member-btn': 'onRemoveMember'
'mouseenter .progress-level-cell': 'onMouseEnterPoint'
@ -41,6 +43,7 @@ module.exports = class ClanDetailsView extends RootView
initData: ->
@showExpandedProgress = false
@memberSort = 'nameAsc'
@stats = {}
@campaigns = new CocoCollection([], { url: "/db/campaign", model: Campaign, comparator:'_id' })
@ -67,6 +70,7 @@ module.exports = class ClanDetailsView extends RootView
context = super()
context.campaignLevelProgressions = @campaignLevelProgressions ? []
context.clan = @clan
context.conceptsProgression = @conceptsProgression ? []
if application.isProduction()
context.joinClanLink = "https://codecombat.com/clans/#{@clanID}"
else
@ -76,21 +80,25 @@ module.exports = class ClanDetailsView extends RootView
context.memberLanguageMap = @memberLanguageMap
context.memberLevelStateMap = @memberLevelMap ? {}
context.memberMaxLevelCount = @memberMaxLevelCount
context.members = @members?.models
context.memberSort = @memberSort
context.isOwner = @clan.get('ownerID') is me.id
context.isMember = @clanID in (me.get('clans') ? [])
context.stats = @stats
# Find last campaign level for each user
# TODO: why do we do this for every render?
highestUserLevelCountMap = {}
lastUserCampaignLevelMap = {}
maxLastUserCampaignLevel = 0
userConceptsMap = {}
if @campaigns.loaded
levelCount = 0
for campaign in @campaigns.models
campaignID = campaign.id
lastLevelIndex = 0
for levelID, level of campaign.get('levels')
levelSlug = level.slug
for member in context.members
for member in @members?.models ? []
if context.memberLevelStateMap[member.id]?[levelSlug]
lastUserCampaignLevelMap[member.id] ?= {}
lastUserCampaignLevelMap[member.id][campaignID] ?= {}
@ -98,10 +106,20 @@ module.exports = class ClanDetailsView extends RootView
levelSlug: levelSlug
index: lastLevelIndex
maxLastUserCampaignLevel = lastLevelIndex if lastLevelIndex > maxLastUserCampaignLevel
if level.concepts?
userConceptsMap[member.id] ?= {}
for concept in level.concepts
continue if userConceptsMap[member.id][concept] is 'complete'
userConceptsMap[member.id][concept] = context.memberLevelStateMap[member.id][levelSlug].state
highestUserLevelCountMap[member.id] = levelCount
lastLevelIndex++
levelCount++
@sortMembers(highestUserLevelCountMap, userConceptsMap) if @clan.get('dashboardType') is 'premium'
context.members = @members?.models ? []
context.lastUserCampaignLevelMap = lastUserCampaignLevelMap
context.showExpandedProgress = maxLastUserCampaignLevel <= 30 or @showExpandedProgress
context.userConceptsMap = userConceptsMap
context
afterRender: ->
@ -114,6 +132,42 @@ module.exports = class ClanDetailsView extends RootView
@memberAchievements.fetch cache: false
@memberSessions.fetch cache: false
sortMembers: (highestUserLevelCountMap, userConceptsMap) ->
# Progress sort precedence: most completed concepts, most started concepts, most levels, name sort
return unless @members? and @memberSort?
switch @memberSort
when "nameDesc"
@members.comparator = (a, b) -> return (b.get('name') or 'Anoner').localeCompare(a.get('name') or 'Anoner')
when "progressAsc"
@members.comparator = (a, b) ->
aComplete = (concept for concept, state of userConceptsMap[a.id] when state is 'complete')
bComplete = (concept for concept, state of userConceptsMap[b.id] when state is 'complete')
aStarted = (concept for concept, state of userConceptsMap[a.id] when state is 'started')
bStarted = (concept for concept, state of userConceptsMap[b.id] when state is 'started')
if aComplete < bComplete then return -1
else if aComplete > bComplete then return 1
else if aStarted < bStarted then return -1
else if aStarted > bStarted then return 1
if highestUserLevelCountMap[a.id] < highestUserLevelCountMap[b.id] then return -1
else if highestUserLevelCountMap[a.id] > highestUserLevelCountMap[b.id] then return 1
(a.get('name') or 'Anoner').localeCompare(b.get('name') or 'Anoner')
when "progressDesc"
@members.comparator = (a, b) ->
aComplete = (concept for concept, state of userConceptsMap[a.id] when state is 'complete')
bComplete = (concept for concept, state of userConceptsMap[b.id] when state is 'complete')
aStarted = (concept for concept, state of userConceptsMap[a.id] when state is 'started')
bStarted = (concept for concept, state of userConceptsMap[b.id] when state is 'started')
if aComplete > bComplete then return -1
else if aComplete < bComplete then return 1
else if aStarted > bStarted then return -1
else if aStarted < bStarted then return 1
if highestUserLevelCountMap[a.id] > highestUserLevelCountMap[b.id] then return -1
else if highestUserLevelCountMap[a.id] < highestUserLevelCountMap[b.id] then return 1
(b.get('name') or 'Anoner').localeCompare(a.get('name') or 'Anoner')
else
@members.comparator = (a, b) -> return (a.get('name') or 'Anoner').localeCompare(b.get('name') or 'Anoner')
@members.sort()
updateHeroIcons: ->
return unless @members?.models?
for member in @members.models
@ -124,6 +178,7 @@ module.exports = class ClanDetailsView extends RootView
onCampaignSync: ->
return unless @campaigns.loaded
@campaignLevelProgressions = []
@conceptsProgression = []
for campaign in @campaigns.models
continue if campaign.get('slug') is 'auditions'
campaignLevelProgression =
@ -136,6 +191,9 @@ module.exports = class ClanDetailsView extends RootView
ID: levelID
slug: level.slug
name: level.name
if level.concepts?
for concept in level.concepts
@conceptsProgression.push concept unless concept in @conceptsProgression
@campaignLevelProgressions.push campaignLevelProgression
@render?()
@ -272,6 +330,14 @@ module.exports = class ClanDetailsView extends RootView
success: (model, response, options) => @refreshData()
@supermodel.addRequestResource( 'leave_clan', options).load()
onClickMemberHeader: (e) ->
@memberSort = if @memberSort is 'nameAsc' then 'nameDesc' else 'nameAsc'
@render?()
onClickProgressHeader: (e) ->
@memberSort = if @memberSort is 'progressAsc' then 'progressDesc' else 'progressAsc'
@render?()
onRemoveMember: (e) ->
return unless window.confirm("Remove Hero?")
if memberID = $(e.target).data('id')

View file

@ -73,9 +73,15 @@ module.exports = class ClansView extends RootView
setupPrivateInfoPopover: ->
popoverTitle = "<h3>Private Clans</h3>"
popoverContent = "<p>Invite only</p>"
popoverContent += "<p>Detailed dashboard:</p>"
popoverContent += "<p><img src='/images/pages/clans/dashboard_preview.png' width='700'></p>"
popoverContent = "<ul>"
popoverContent += "<li><span style='font-weight:bold;'>Track concepts</span> learned by each member"
popoverContent += "<li>Track levels completed for each member"
popoverContent += "<li>See your members' <span style='font-weight:bold;'>solutions</span>"
popoverContent += "<li>Sort members by name or progress"
popoverContent += "<li><span style='font-weight:bold;'>Requires invitation</span> to join"
popoverContent += "</ul>"
popoverContent += "<p><img src='/images/pages/clans/dashboard_preview.png' height='400'></p>"
popoverContent += "<p>Private clans require a subscription to create or join.</p>"
@$el.find('.private-more-info').popover(
animation: true
html: true

View file

@ -78,6 +78,7 @@ module.exports = class SubscribeModal extends ModalView
setupPaymentMethodsInfoPopover: ->
popoverTitle = $.i18n.t('subscribe.payment_methods_title')
popoverTitle += '<button type="button" class="close" onclick="$(&#39;#payment-methods-info&#39;).popover(&#39;hide&#39;);">&times;</button>'
popoverContent = "<p>" + $.i18n.t('subscribe.payment_methods_blurb1') + "</p>"
popoverContent += "<p>" + $.i18n.t('subscribe.payment_methods_blurb2') + " <a href='mailto:support@codecombat.com'>support@codecombat.com</a>."
@$el.find('#payment-methods-info').popover(

View file

@ -7,9 +7,10 @@ module.exports = class CourseDetailsView extends RootView
template: template
events:
'change .expand-progress-checkbox': 'onExpandedProgressCheckbox'
'change .select-session': 'onChangeSession'
'click .edit-class-name-btn': 'onClickEditClassName'
'click .edit-description-btn': 'onClickEditClassDescription'
'change .select-session': 'onChangeSession'
constructor: (options, @courseID) ->
super options
@ -20,21 +21,43 @@ module.exports = class CourseDetailsView extends RootView
context.course = @course ? {}
context.instance = @instances?[@currentInstanceIndex] ? {}
context.instances = @instances ? []
context.maxLastStartedIndex = @maxLastStartedIndex ? 0
context.userLevelStateMap = @userLevelStateMap ? {}
context.showExpandedProgress = @maxLastStartedIndex <= 30 or @showExpandedProgress
context
initData: ->
mockData = require 'views/courses/mock1/CoursesMockData'
@course = mockData.courses[@courseID]
# @instance = mockData.instances[_.random(0, mockData.instances.length - 1)]
@currentInstanceIndex = 0
@instances = mockData.instances
@updateLevelMaps()
updateLevelMaps: ->
@userLevelStateMap = {}
@maxLastStartedIndex = -1
for student in @instances?[@currentInstanceIndex].students
lastCompletedIndex = _.random(0, @course.levels.length)
lastStartedIndex = lastCompletedIndex + 1
@userLevelStateMap[student] =
lastCompletedIndex: lastCompletedIndex
lastStartedIndex: lastStartedIndex
@maxLastStartedIndex = lastStartedIndex if lastStartedIndex > @maxLastStartedIndex
onChangeSession: (e) ->
@showExpandedProgress = false
newSessionValue = $(e.target).val()
for val, index in @instances when val.name is newSessionValue
@currentInstanceIndex = index
@updateLevelMaps()
@render?()
onExpandedProgressCheckbox: (e) ->
@showExpandedProgress = $('.expand-progress-checkbox').prop('checked')
# TODO: why does render reset the checkbox to be unchecked?
@render?()
$('.expand-progress-checkbox').attr('checked', @showExpandedProgress)
onClickEditClassName: (e) ->
alert 'TODO: Popup for editing name for this course session'