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" make_private: "Make clan private"
subs_only: "subscribers only" subs_only: "subscribers only"
create_clan: "Create New Clan" create_clan: "Create New Clan"
private_preview: "Preview"
public_clans: "Public Clans" public_clans: "Public Clans"
my_clans: "My Clans" my_clans: "My Clans"
clan_name: "Clan Name" clan_name: "Clan Name"

View file

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

View file

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

View file

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

View file

@ -10,3 +10,60 @@
.select-session .select-session
width: 300px width: 300px
display: inline 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 table.table.table-condensed
thead thead
tr tr
th(data-i18n="resources.hero") Hero
th th
span.progress-header(data-i18n="clans.progress") Progress span.member-header.spr(data-i18n="resources.hero") Hero
span.progress-key(data-i18n="clans.not_started_1") not started 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-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') input.expand-progress-checkbox(type='checkbox')
span.spl.expand-progress-label(data-i18n="clans.exp_levels") Expand levels span.spl.expand-progress-label(data-i18n="clans.exp_levels") Expand levels
tbody tbody
each member in members each member in members
tr tr
td td
div div
span.hero-icon-cell span.hero-icon-cell
span.spr.player-hero-icon(data-memberid="#{member.id}") span.spr.player-hero-icon(data-memberid="#{member.id}")
span.code-language-cell span.code-language-cell
if memberLanguageMap && memberLanguageMap[member.id] 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]) span.code-language-cell(style="background-image: url(/images/common/code_languages/#{memberLanguageMap[member.id]}_small.png)", title=memberLanguageMap[member.id])
div div
a(href="/user/#{member.id}")= member.get('name') || 'Anoner' a(href="/user/#{member.id}")= member.get('name') || 'Anoner'
div Level #{member.level()} div Level #{member.level()}
if isOwner && member.id !== clan.get('ownerID') 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 button.btn.btn-xs.btn-warning.remove-member-btn(data-id="#{member.id}", data-i18n="clans.rem_hero") Remove Hero
td.progress-cell 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 each level in campaign.levels
if lastUserCampaignLevelMap[member.id] && lastUserCampaignLevelMap[member.id][campaign.ID] - i++
div.level-progression-campaign= campaign.name - var state = null, levelInfo = null
- var i = 0 if memberLevelStateMap[member.id][level.slug]
- levelInfo = memberLevelStateMap[member.id][level.slug].levelInfo
each level in campaign.levels - state = memberLevelStateMap[member.id][level.slug].state
- i++ if state === 'complete'
- var state = null, levelInfo = null span.progress-level-cell.progress-level-cell-complete(data-level-info=levelInfo) #{i}
if memberLevelStateMap[member.id][level.slug] if showExpandedProgress || i === 1 || i === lastUserCampaignLevelMap[member.id][campaign.ID].index + 1
- levelInfo = memberLevelStateMap[member.id][level.slug].levelInfo span.spl #{level.name}
- state = memberLevelStateMap[member.id][level.slug].state .level-popup-container
if state === 'complete' h3 #{i}. #{levelInfo.level}
span.progress-level-cell.progress-level-cell-complete(data-level-info=levelInfo) #{i} p
if showExpandedProgress || i === 1 || i === lastUserCampaignLevelMap[member.id][campaign.ID].index + 1 div
span.spl #{level.name} span(data-i18n="clans.status") Status
.level-popup-container span.spr :
h3 #{i}. #{levelInfo.level} span(data-i18n="clans.complete_2") Complete
p 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 div
span(data-i18n="choose_hero.status") Status span(data-i18n="clans.status") Status
span.spr : span.spr :
span(data-i18n="clans.complete_2") Complete span(data-i18n="clans.not_started_2") Not Started
div if lastUserCampaignLevelMap[member.id][campaign.ID].levelSlug === level.slug
span(data-i18n="clans.playtime") Playtime - break
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
//- Basic dashboard //- Basic dashboard
else else

View file

@ -10,7 +10,7 @@ block content
input(type='checkbox').private-clan-checkbox input(type='checkbox').private-clan-checkbox
span.spl(data-i18n="clans.make_private") Make clan private span.spl(data-i18n="clans.make_private") Make clan private
span.spl ( span.spl (
a.private-more-info(data-i18n="clans.subs_only") subscribers only a.private-more-info(data-i18n="clans.private_preview")
span ) span )
p p
button.btn.btn-success.create-clan-btn(data-i18n="clans.create_clan") Create New Clan 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') input.student-view-checkbox(type='checkbox')
span.spl Student view span.spl Student view
div TODO: fix ugly tabs div TODO: fix ugly tabs
div TODO: add student progress monitoring
div TODO: level concepts, status, working play button div TODO: level concepts, status, working play button
div TODO: student view 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;') div(style='border-bottom: 1px solid black;')
h1= course.title h1= course.title
@ -64,24 +66,48 @@ block content
table.table.table-condensed table.table.table-condensed
thead thead
tr tr
th Name th
th Progress 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 tbody
each student in instance.students each student in instance.students
tr tr
td td
a= student a= student
td TODO: level progress 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') .tab-pane#invite(role='tabpanel')
p Invite students to join this class. p Invite students to join this class.
if course.title !== 'Introduction to Computer Science' if course.title !== 'Introduction to Computer Science'
p Student unlock code: #{instance.code} p Student unlock code: #{instance.code}
p Class capacity: 34/50 p Class capacity: 34/50
textarea.textarea-emails(rows=3, placeholder="Enter student emails to invite, one per line") textarea.textarea-emails(rows=3, placeholder="Enter student emails to invite, one per line")
div div
button.btn.btn-success.btn-invite Send Invites button.btn.btn-success.btn-invite Send Invites
.tab-pane#levels(role='tabpanel') .tab-pane#levels(role='tabpanel')
table.table.table-condensed table.table.table-condensed
thead thead

View file

@ -48,11 +48,25 @@ block content
span.spl(data-i18n="teachers.monitor_progress_4") span.spl(data-i18n="teachers.monitor_progress_4")
p(data-i18n="teachers.monitor_progress_5") p(data-i18n="teachers.monitor_progress_5")
h4(data-i18n="teachers.sub_includes_7") 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 p
span.spr(data-i18n="teachers.private_clans_2") span.spr(data-i18n="teachers.private_clans_2")
a(href='/clans', data-i18n="clans.clan") a(href='/clans', data-i18n="clans.clan")
span(data-i18n="teachers.private_clans_3") span(data-i18n="teachers.private_clans_3")
p Private clans require a subscription to create or join.
h3(data-i18n="teachers.material_title") h3(data-i18n="teachers.material_title")
if me.get('chinaVersion') if me.get('chinaVersion')
@ -78,19 +92,19 @@ block content
tbody tbody
tr tr
td Syntax td Syntax
td If/Else td If Statements
td Arithmetic td Arithmetic
td Object Literals td Object Literals
tr tr
td Methods td Methods
td Relational Operators td Relational Operators
td While-loops td While Loops
td Remote Method Invocation td Remote Method Invocation
tr tr
td Parameters td Parameters
td Object Properties td Object Properties
td Break td Break Statements
td For-Loops td For Loops
tr tr
td Strings td Strings
td Input Handling td Input Handling

View file

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

View file

@ -73,9 +73,15 @@ module.exports = class ClansView extends RootView
setupPrivateInfoPopover: -> setupPrivateInfoPopover: ->
popoverTitle = "<h3>Private Clans</h3>" popoverTitle = "<h3>Private Clans</h3>"
popoverContent = "<p>Invite only</p>" popoverContent = "<ul>"
popoverContent += "<p>Detailed dashboard:</p>" popoverContent += "<li><span style='font-weight:bold;'>Track concepts</span> learned by each member"
popoverContent += "<p><img src='/images/pages/clans/dashboard_preview.png' width='700'></p>" 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( @$el.find('.private-more-info').popover(
animation: true animation: true
html: true html: true

View file

@ -78,6 +78,7 @@ module.exports = class SubscribeModal extends ModalView
setupPaymentMethodsInfoPopover: -> setupPaymentMethodsInfoPopover: ->
popoverTitle = $.i18n.t('subscribe.payment_methods_title') 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_blurb1') + "</p>"
popoverContent += "<p>" + $.i18n.t('subscribe.payment_methods_blurb2') + " <a href='mailto:support@codecombat.com'>support@codecombat.com</a>." popoverContent += "<p>" + $.i18n.t('subscribe.payment_methods_blurb2') + " <a href='mailto:support@codecombat.com'>support@codecombat.com</a>."
@$el.find('#payment-methods-info').popover( @$el.find('#payment-methods-info').popover(

View file

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