Merge branch 'master' into production

This commit is contained in:
Nick Winter 2014-06-13 16:04:53 -07:00
commit 9eb495650f
11 changed files with 128 additions and 69 deletions

View file

@ -4,52 +4,52 @@ block content
h1(data-i18n="employers.want_to_hire_our_players") Hire CodeCombat Players h1(data-i18n="employers.want_to_hire_our_players") Hire CodeCombat Players
div#info_wrapper
div#leftside
div.information_row
img(class="employer_icon" src="/images/pages/employer/employer_icon1.png")
h2(data-i18n="employers.what") What is CodeCombat?
p(data-i18n="employers.what_blurb") CodeCombat is a multiplayer browser programming game. Players write code to control their forces in battle against other developers. We support JavaScript, Python, Lua, Clojure, CoffeeScript, and Io.
div.information_row
img(class="employer_icon" src="/images/pages/employer/employer_icon3.png")
h2(data-i18n="employers.who") Who Are the Players?
p(data-i18n="employers.who_blurb") CodeCombateers are software developers who enjoy using their programming skills to play games. They range from college seniors at top 20 engineering programs to 20-year industry veterans.
div#rightside
div.information_row
img(class="employer_icon" src="/images/pages/employer/employer_icon2.png")
h2(data-i18n="employers.how") How Do We Find Developers?
p(data-i18n="employers.how_blurb") We host competitive tournaments to attract competitive software engieneers. We then use in-house algorithms to identify the best players among the top 5% of tournament winners.
div.information_row
img(class="employer_icon" src="/images/pages/employer/employer_icon4.png")
h2(data-i18n="employers.why") Why Hire Through Us?
p
span(data-i18n="employers.why_blurb_1") We will save you time. Every CodeCombateer we feaure is
strong(data-i18n="employers.why_blurb_2") looking for work
span(data-i18n="employers.why_blurb_3") , has
strong(data-i18n="employers.why_blurb_4") demonstrated top notch technical skills
span(data-i18n="employers.why_blurb_5") , and has been
strong(data-i18n="employers.why_blurb_6") personally screened by us
span(data-i18n="employers.why_blurb_7") . Stop screening and start hiring.
if !isEmployer && !me.isAdmin() if !isEmployer && !me.isAdmin()
div#info_wrapper
div#leftside
div.information_row
img(class="employer_icon" src="/images/pages/employer/employer_icon1.png")
h2(data-i18n="employers.what") What is CodeCombat?
p(data-i18n="employers.what_blurb") CodeCombat is a multiplayer browser programming game. Players write code to control their forces in battle against other developers. We support JavaScript, Python, Lua, Clojure, CoffeeScript, and Io.
div.information_row
img(class="employer_icon" src="/images/pages/employer/employer_icon3.png")
h2(data-i18n="employers.who") Who Are the Players?
p(data-i18n="employers.who_blurb") CodeCombateers are software developers who enjoy using their programming skills to play games. They range from college seniors at top 20 engineering programs to 20-year industry veterans.
div#rightside
div.information_row
img(class="employer_icon" src="/images/pages/employer/employer_icon2.png")
h2(data-i18n="employers.how") How Do We Find Developers?
p(data-i18n="employers.how_blurb") We host competitive tournaments to attract competitive software engieneers. We then use in-house algorithms to identify the best players among the top 5% of tournament winners.
div.information_row
img(class="employer_icon" src="/images/pages/employer/employer_icon4.png")
h2(data-i18n="employers.why") Why Hire Through Us?
p
span(data-i18n="employers.why_blurb_1") We will save you time. Every CodeCombateer we feaure is
strong(data-i18n="employers.why_blurb_2") looking for work
span(data-i18n="employers.why_blurb_3") , has
strong(data-i18n="employers.why_blurb_4") demonstrated top notch technical skills
span(data-i18n="employers.why_blurb_5") , and has been
strong(data-i18n="employers.why_blurb_6") personally screened by us
span(data-i18n="employers.why_blurb_7") . Stop screening and start hiring.
h3.see-candidates-header h3.see-candidates-header
a#see-candidates(title='Contact', tabindex=-1, data-toggle="coco-modal", data-target="modal/employer_signup", data-i18n="employers.see_candidates") Click here to see our candidates a#see-candidates(title='Contact', tabindex=-1, data-toggle="coco-modal", data-target="modal/employer_signup", data-i18n="employers.see_candidates") Click here to see our candidates
@ -106,7 +106,7 @@ block content
td= profile.lookingFor td= profile.lookingFor
td= profile.jobTitle td= profile.jobTitle
td td
each skill in profile.skills.slice(0, 10) each skill in profile.skills
code= skill code= skill
span span
td= profile.experience td= profile.experience

View file

@ -10,16 +10,31 @@ block modal-body-content
if userIsAnonymous if userIsAnonymous
if userIsAuthorized if userIsAuthorized
| You appear to be authorized on CodeCombat with LinkedIn. | You appear to be authorized on CodeCombat with LinkedIn.
else if sentMoreInfoEmail
| Thanks! You should receive an email from George shortly.
else else
h4(data-i18n="employer_signup.sub_heading") Let us find your next brilliant developers. h4(data-i18n="employer_signup.sub_heading") Let us find your next brilliant developers.
p Create an account to get started! p Create an account to get started!
.form .form#signup-form
.form-group .form-group
label.control-label(for="signup-email", data-i18n="general.email") Email label.control-label(for="signup-email", data-i18n="general.email") Email
input#signup-email.form-control.input-large(name="email",type="email") input#signup-email.form-control.input-large(name="email",type="email")
.form-group .form-group
label.control-label(for="signup-password", data-i18n="general.password") Password label.control-label(for="signup-password", data-i18n="general.password") Password
input#signup-password.input-large.form-control(name="password", type="password") input#signup-password.input-large.form-control(name="password", type="password")
.modal-footer.linkedin
button.btn.btn-primary(id="create-account-button") Create Account
br
br
| Already have a CodeCombat account?
a.login-link(data-toggle="coco-modal", data-target="modal/auth") Log in to continue!
h4 Want more information first?
p Enter your email and George, our CEO, will contact you shortly.
.form
.form-group
label.control-label(for="more-info-email", data-i18n="general.email") Email
input#more-info-email.form-control.input-large(name="more-info-email",type="email")
button.btn.btn-primary(id="more-info-button") Send me more information!
else if !userIsAuthorized else if !userIsAuthorized
.modal-footer.linkedin .modal-footer.linkedin
p Please sign into your LinkedIn account to verify your identity. p Please sign into your LinkedIn account to verify your identity.
@ -44,14 +59,7 @@ block modal-body-content
| By clicking Agree, you are agreeing to CodeCombat's Placement Agreement on behalf of your company. You also consent to CodeCombat storing basic LinkedIn profile data for verification purposes, including your name, email, public profile URL, and work history. | By clicking Agree, you are agreeing to CodeCombat's Placement Agreement on behalf of your company. You also consent to CodeCombat storing basic LinkedIn profile data for verification purposes, including your name, email, public profile URL, and work history.
block modal-footer block modal-footer
if userIsAnonymous if userIsAnonymous
if !userIsAuthorized if userIsAuthorized
.modal-footer.linkedin
button.btn.btn-primary(id="create-account-button") Create Account
br
br
| Already have a CodeCombat account?
a.login-link(data-toggle="coco-modal", data-target="modal/auth") Log in to continue!
else
.modal-footer.linkedin .modal-footer.linkedin
a.login-link(data-toggle="coco-modal", data-target="modal/auth") Please log in to continue. a.login-link(data-toggle="coco-modal", data-target="modal/auth") Please log in to continue.
else if !userIsAnonymous && !userIsAuthorized else if !userIsAnonymous && !userIsAuthorized

View file

@ -253,7 +253,7 @@ class LatestVersionCollection extends CocoCollection
class LatestVersionReferenceNode extends TreemaNode class LatestVersionReferenceNode extends TreemaNode
searchValueTemplate: '<input placeholder="Search" /><div class="treema-search-results"></div>' searchValueTemplate: '<input placeholder="Search" /><div class="treema-search-results"></div>'
valueClass: 'treema-latest-version' valueClass: 'treema-latest-version'
url: '/db/article/search' url: '/db/article'
lastTerm: null lastTerm: null
constructor: -> constructor: ->
@ -264,7 +264,7 @@ class LatestVersionReferenceNode extends TreemaNode
link = (l for l in links when l.rel is 'db')[0] link = (l for l in links when l.rel is 'db')[0]
return unless link return unless link
parts = (p for p in link.href.split('/') when p.length) parts = (p for p in link.href.split('/') when p.length)
@url = "/db/#{parts[1]}/search" @url = "/db/#{parts[1]}"
@model = require('models/' + _.string.classify(parts[1])) @model = require('models/' + _.string.classify(parts[1]))
buildValueForDisplay: (valEl) -> buildValueForDisplay: (valEl) ->

View file

@ -301,7 +301,7 @@ module.exports = class ProfileView extends View
null null
onContactCandidate: (e) -> onContactCandidate: (e) ->
@openModalView new JobProfileContactView recipientID: @user.id @openModalView new JobProfileContactView recipientID: @user.id, recipientUserName: @user.get('name')
showErrors: (errors) -> showErrors: (errors) ->
section = @$el.find '.saving' section = @$el.find '.saving'

View file

@ -4,6 +4,7 @@ CocoClass = require 'lib/CocoClass'
loadingScreenTemplate = require 'templates/loading' loadingScreenTemplate = require 'templates/loading'
loadingErrorTemplate = require 'templates/loading_error' loadingErrorTemplate = require 'templates/loading_error'
lastToggleModalCall = 0
visibleModal = null visibleModal = null
waitingModal = null waitingModal = null
classCount = 0 classCount = 0
@ -15,9 +16,6 @@ module.exports = class CocoView extends Backbone.View
template: -> '' template: -> ''
events: events:
'click a': 'toggleModal'
'click button': 'toggleModal'
'click li': 'toggleModal'
'click .retry-loading-resource': 'onRetryResource' 'click .retry-loading-resource': 'onRetryResource'
'click .retry-loading-request': 'onRetryRequest' 'click .retry-loading-request': 'onRetryRequest'
@ -46,7 +44,6 @@ module.exports = class CocoView extends Backbone.View
@subviews = {} @subviews = {}
@listenToShortcuts() @listenToShortcuts()
@updateProgressBar = _.debounce @updateProgressBar, 100 @updateProgressBar = _.debounce @updateProgressBar, 100
@toggleModal = _.debounce @toggleModal, 100
# Backbone.Mediator handles subscription setup/teardown automatically # Backbone.Mediator handles subscription setup/teardown automatically
@listenTo(@supermodel, 'loaded-all', @onLoaded) @listenTo(@supermodel, 'loaded-all', @onLoaded)
@ -149,8 +146,16 @@ module.exports = class CocoView extends Backbone.View
$(e.target).closest('.loading-error-alert').remove() $(e.target).closest('.loading-error-alert').remove()
# Modals # Modals
@lastToggleModalCall = 0
toggleModal: (e) -> toggleModal: (e) ->
if new Date().getTime() - CocoView.lastToggleModalCall < 5
# Defensive move. This function has had a history of messing things up.
console.error 'toggleModal is getting called too often!'
return
CocoView.lastToggleModalCall = new Date().getTime()
if $(e.currentTarget).prop('target') is '_blank' if $(e.currentTarget).prop('target') is '_blank'
return true return true
# special handler for opening modals that are dynamically loaded, rather than static in the page. It works (or should work) like Bootstrap's modals, except use coco-modal for the data-toggle value. # special handler for opening modals that are dynamically loaded, rather than static in the page. It works (or should work) like Bootstrap's modals, except use coco-modal for the data-toggle value.

View file

@ -6,6 +6,11 @@ module.exports = class ModalView extends CocoView
closesOnClickOutside: true closesOnClickOutside: true
modalWidthPercent: null modalWidthPercent: null
plain: false plain: false
events:
'click a': 'toggleModal'
'click button': 'toggleModal'
'click li': 'toggleModal'
shortcuts: shortcuts:
'esc': 'hide' 'esc': 'hide'

View file

@ -22,6 +22,9 @@ module.exports = class RootView extends CocoView
'change .language-dropdown': 'onLanguageChanged' 'change .language-dropdown': 'onLanguageChanged'
'click .toggle-fullscreen': 'toggleFullscreen' 'click .toggle-fullscreen': 'toggleFullscreen'
'click .auth-button': 'onClickAuthbutton' 'click .auth-button': 'onClickAuthbutton'
'click a': 'toggleModal'
'click button': 'toggleModal'
'click li': 'toggleModal'
subscriptions: subscriptions:
'achievements:new': 'handleNewAchievements' 'achievements:new': 'handleNewAchievements'

View file

@ -19,6 +19,7 @@ module.exports = class EmployerSignupView extends View
events: events:
"click #contract-agreement-button": "agreeToContract" "click #contract-agreement-button": "agreeToContract"
"click #create-account-button": "createAccount" "click #create-account-button": "createAccount"
"click #more-info-button": "submitMoreInfoEmail"
"click .login-link": "setHashToOpenModalAutomatically" "click .login-link": "setHashToOpenModalAutomatically"
"keydown": "checkForFormSubmissionEnterPress" "keydown": "checkForFormSubmissionEnterPress"
@ -30,6 +31,7 @@ module.exports = class EmployerSignupView extends View
@reloadWhenClosed = false @reloadWhenClosed = false
@linkedinLoaded = Boolean(IN.parse) @linkedinLoaded = Boolean(IN.parse)
@waitingForLinkedIn = false @waitingForLinkedIn = false
@sentMoreInfoEmail = false
window.contractCallback = => window.contractCallback = =>
@authorizedWithLinkedIn = IN?.User?.isAuthorized() @authorizedWithLinkedIn = IN?.User?.isAuthorized()
@render() @render()
@ -59,6 +61,7 @@ module.exports = class EmployerSignupView extends View
context.userIsAuthorized = @authorizedWithLinkedIn context.userIsAuthorized = @authorizedWithLinkedIn
context.userHasSignedContract = "employer" in me.get("permissions") context.userHasSignedContract = "employer" in me.get("permissions")
context.userIsAnonymous = context.me.get('anonymous') context.userIsAnonymous = context.me.get('anonymous')
context.sentMoreInfoEmail = @sentMoreInfoEmail
context context
agreeToContract: -> agreeToContract: ->
@ -80,23 +83,45 @@ module.exports = class EmployerSignupView extends View
alert "There was an error signing the contract. Please contact team@codecombat.com with this error: #{error.responseText}" alert "There was an error signing the contract. Please contact team@codecombat.com with this error: #{error.responseText}"
checkForFormSubmissionEnterPress: (e) -> checkForFormSubmissionEnterPress: (e) ->
if e.which is 13 then @createAccount(e) if e.which is 13
if $("#signup-email").val() isnt '' and $("#signup-password").val() isnt ''
@createAccount(e)
else if $("#more-info-email").val() isnt ''
@submitMoreInfoEmail e
createAccount: (e) => createAccount: (e) =>
window.tracker?.trackEvent 'Finished Employer Signup' window.tracker?.trackEvent 'Finished Employer Signup'
el = $("#signup-form")
e.stopPropagation() e.stopPropagation()
forms.clearFormAlerts(@$el) forms.clearFormAlerts(el)
userObject = forms.formToObject @$el userObject = forms.formToObject el
delete userObject.subscribe delete userObject.subscribe
for key, val of me.attributes when key in ["preferredLanguage", "testGroupNumber", "dateCreated", "wizardColor1", "name", "music", "volume", "emails"] for key, val of me.attributes when key in ["preferredLanguage", "testGroupNumber", "dateCreated", "wizardColor1", "name", "music", "volume", "emails"]
userObject[key] ?= val userObject[key] ?= val
userObject.emails ?= {} userObject.emails ?= {}
userObject.emails.employerNotes = {enabled: true} userObject.emails.employerNotes = {enabled: true}
res = tv4.validateMultiple userObject, User.schema res = tv4.validateMultiple userObject, User.schema
return forms.applyErrorsToForm(@$el, res.errors) unless res.valid return forms.applyErrorsToForm(el, res.errors) unless res.valid
@enableModalInProgress(@$el) @enableModalInProgress(el)
auth.createUserWithoutReload userObject, null auth.createUserWithoutReload userObject, null
submitMoreInfoEmail: (e) =>
emailAddress = $("#more-info-email").val()
window.tracker?.trackEvent 'Employer requested more information.'
successFunc = =>
@sentMoreInfoEmail = true
@render()
errorFunc = =>
alert("Something went wrong! Please contact team@codecombat.com for more information and inform them of this error.")
$.ajax
type: "POST"
url: "/contact"
data:
email: emailAddress
message: "THIS IS AN AUTOMATED MESSAGE FROM THE EMPLOYER SIGNUP FORM \n Please send me more info about hiring CodeCombat players."
success: successFunc
error: errorFunc
setHashToOpenModalAutomatically: (e) -> setHashToOpenModalAutomatically: (e) ->
window.location.hash = "employerSignupLoggingIn" window.location.hash = "employerSignupLoggingIn"

View file

@ -36,7 +36,7 @@ module.exports = class JobProfileContactView extends ContactView
contactMessage.recipientID = @options.recipientID contactMessage.recipientID = @options.recipientID
res = tv4.validateMultiple contactMessage, contactSchema res = tv4.validateMultiple contactMessage, contactSchema
return forms.applyErrorsToForm @$el, res.errors unless res.valid return forms.applyErrorsToForm @$el, res.errors unless res.valid
contactMessage.message += '\n\n\n\n[CodeCombat says: please let us know if you end up accepting this job. Thanks!]' contactMessage.message += "\n\n\n\n[CodeCombat says: please let us know if you end up accepting this job. Thanks, #{@options.recipientUserName}!]"
window.tracker?.trackEvent 'Sent Job Profile Message', message: contactMessage window.tracker?.trackEvent 'Sent Job Profile Message', message: contactMessage
sendContactMessage contactMessage, @$el sendContactMessage contactMessage, @$el
$.post "/db/user/#{me.id}/track/contact_candidate" $.post "/db/user/#{me.id}/track/contact_candidate"

View file

@ -4,6 +4,7 @@ template = require 'templates/modal/model'
module.exports = class ModelModal extends View module.exports = class ModelModal extends View
id: 'model-modal' id: 'model-modal'
template: template template: template
plain: true
constructor: (options) -> constructor: (options) ->
super options super options

View file

@ -0,0 +1,12 @@
AuthModalView = require 'views/modal/auth_modal'
RecoverModalView = require 'views/modal/recover_modal'
describe 'AuthModalView', ->
it 'opens the recover modal when you click the recover link', ->
m = new AuthModalView()
m.render()
spyOn(m, 'openModalView')
m.$el.find('#link-to-recover').click()
expect(m.openModalView.calls.count()).toEqual(1)
args = m.openModalView.calls.argsFor(0)
expect(args[0] instanceof RecoverModalView).toBeTruthy()