From 6c87d5792db763fc5e123f668480f012e93cc132 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Fri, 13 Jun 2014 09:52:34 -0700 Subject: [PATCH 1/6] One last fix to the search url. --- app/treema-ext.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/treema-ext.coffee b/app/treema-ext.coffee index 044c7e6d4..0a4b8d036 100644 --- a/app/treema-ext.coffee +++ b/app/treema-ext.coffee @@ -253,7 +253,7 @@ class LatestVersionCollection extends CocoCollection class LatestVersionReferenceNode extends TreemaNode searchValueTemplate: '
' valueClass: 'treema-latest-version' - url: '/db/article/search' + url: '/db/article' lastTerm: null constructor: -> @@ -264,7 +264,7 @@ class LatestVersionReferenceNode extends TreemaNode link = (l for l in links when l.rel is 'db')[0] return unless link 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])) buildValueForDisplay: (valEl) -> From 8f4e1b4bf0f751ec2a252886739aeda846c1da92 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Fri, 13 Jun 2014 10:11:01 -0700 Subject: [PATCH 2/6] Making the ModelModal look better. --- app/views/modal/model_modal.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/app/views/modal/model_modal.coffee b/app/views/modal/model_modal.coffee index 67602431d..e1f983247 100644 --- a/app/views/modal/model_modal.coffee +++ b/app/views/modal/model_modal.coffee @@ -4,6 +4,7 @@ template = require 'templates/modal/model' module.exports = class ModelModal extends View id: 'model-modal' template: template + plain: true constructor: (options) -> super options From a1dd9a714e3af2854aaead7ff032141d5af1ec93 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Fri, 13 Jun 2014 13:35:57 -0700 Subject: [PATCH 3/6] Fixed #1153. --- app/views/kinds/CocoView.coffee | 13 +++++++++---- app/views/kinds/ModalView.coffee | 5 +++++ app/views/kinds/RootView.coffee | 3 +++ test/app/views/modal/AuthModalView.spec.coffee | 12 ++++++++++++ 4 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 test/app/views/modal/AuthModalView.spec.coffee diff --git a/app/views/kinds/CocoView.coffee b/app/views/kinds/CocoView.coffee index 468176927..616fd7847 100644 --- a/app/views/kinds/CocoView.coffee +++ b/app/views/kinds/CocoView.coffee @@ -4,6 +4,7 @@ CocoClass = require 'lib/CocoClass' loadingScreenTemplate = require 'templates/loading' loadingErrorTemplate = require 'templates/loading_error' +lastToggleModalCall = 0 visibleModal = null waitingModal = null classCount = 0 @@ -15,9 +16,6 @@ module.exports = class CocoView extends Backbone.View template: -> '' events: - 'click a': 'toggleModal' - 'click button': 'toggleModal' - 'click li': 'toggleModal' 'click .retry-loading-resource': 'onRetryResource' 'click .retry-loading-request': 'onRetryRequest' @@ -46,7 +44,6 @@ module.exports = class CocoView extends Backbone.View @subviews = {} @listenToShortcuts() @updateProgressBar = _.debounce @updateProgressBar, 100 - @toggleModal = _.debounce @toggleModal, 100 # Backbone.Mediator handles subscription setup/teardown automatically @listenTo(@supermodel, 'loaded-all', @onLoaded) @@ -149,8 +146,16 @@ module.exports = class CocoView extends Backbone.View $(e.target).closest('.loading-error-alert').remove() # Modals + + @lastToggleModalCall = 0 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' 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. diff --git a/app/views/kinds/ModalView.coffee b/app/views/kinds/ModalView.coffee index 26a32081a..a5e3e49ed 100644 --- a/app/views/kinds/ModalView.coffee +++ b/app/views/kinds/ModalView.coffee @@ -6,6 +6,11 @@ module.exports = class ModalView extends CocoView closesOnClickOutside: true modalWidthPercent: null plain: false + + events: + 'click a': 'toggleModal' + 'click button': 'toggleModal' + 'click li': 'toggleModal' shortcuts: 'esc': 'hide' diff --git a/app/views/kinds/RootView.coffee b/app/views/kinds/RootView.coffee index 2ef78f7f7..899c0aaa7 100644 --- a/app/views/kinds/RootView.coffee +++ b/app/views/kinds/RootView.coffee @@ -22,6 +22,9 @@ module.exports = class RootView extends CocoView 'change .language-dropdown': 'onLanguageChanged' 'click .toggle-fullscreen': 'toggleFullscreen' 'click .auth-button': 'onClickAuthbutton' + 'click a': 'toggleModal' + 'click button': 'toggleModal' + 'click li': 'toggleModal' subscriptions: 'achievements:new': 'handleNewAchievements' diff --git a/test/app/views/modal/AuthModalView.spec.coffee b/test/app/views/modal/AuthModalView.spec.coffee new file mode 100644 index 000000000..4cddd2a9e --- /dev/null +++ b/test/app/views/modal/AuthModalView.spec.coffee @@ -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() \ No newline at end of file From 21e7487132f763258cef46d7284e91ef8bcaf3aa Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Fri, 13 Jun 2014 14:52:35 -0700 Subject: [PATCH 4/6] Showing more skills in employer table for better searching. Hid employer selling copy after sold. --- app/templates/employers.jade | 92 ++++++++++++++++++------------------ 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/app/templates/employers.jade b/app/templates/employers.jade index 774e1251f..3125d7ea6 100644 --- a/app/templates/employers.jade +++ b/app/templates/employers.jade @@ -4,52 +4,52 @@ block content 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() + 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 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.jobTitle td - each skill in profile.skills.slice(0, 10) + each skill in profile.skills code= skill span td= profile.experience From 84601d2927090fd121c22c69d81649250981ca5b Mon Sep 17 00:00:00 2001 From: Michael Schmatz Date: Fri, 13 Jun 2014 15:38:41 -0700 Subject: [PATCH 5/6] Added more info email field to employer signup --- .../modal/employer_signup_modal.jade | 26 ++++++++----- app/views/modal/employer_signup_modal.coffee | 37 ++++++++++++++++--- 2 files changed, 48 insertions(+), 15 deletions(-) diff --git a/app/templates/modal/employer_signup_modal.jade b/app/templates/modal/employer_signup_modal.jade index 0274d0eeb..82854fcc1 100644 --- a/app/templates/modal/employer_signup_modal.jade +++ b/app/templates/modal/employer_signup_modal.jade @@ -10,16 +10,31 @@ block modal-body-content if userIsAnonymous if userIsAuthorized | You appear to be authorized on CodeCombat with LinkedIn. + else if sentMoreInfoEmail + | Thanks! You should receive an email from George shortly. else h4(data-i18n="employer_signup.sub_heading") Let us find your next brilliant developers. p Create an account to get started! - .form + .form#signup-form .form-group label.control-label(for="signup-email", data-i18n="general.email") Email input#signup-email.form-control.input-large(name="email",type="email") .form-group label.control-label(for="signup-password", data-i18n="general.password") 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 .modal-footer.linkedin 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. block modal-footer if userIsAnonymous - 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 + if userIsAuthorized .modal-footer.linkedin a.login-link(data-toggle="coco-modal", data-target="modal/auth") Please log in to continue. else if !userIsAnonymous && !userIsAuthorized diff --git a/app/views/modal/employer_signup_modal.coffee b/app/views/modal/employer_signup_modal.coffee index e7d88ac57..5c0aecdab 100644 --- a/app/views/modal/employer_signup_modal.coffee +++ b/app/views/modal/employer_signup_modal.coffee @@ -19,6 +19,7 @@ module.exports = class EmployerSignupView extends View events: "click #contract-agreement-button": "agreeToContract" "click #create-account-button": "createAccount" + "click #more-info-button": "submitMoreInfoEmail" "click .login-link": "setHashToOpenModalAutomatically" "keydown": "checkForFormSubmissionEnterPress" @@ -30,6 +31,7 @@ module.exports = class EmployerSignupView extends View @reloadWhenClosed = false @linkedinLoaded = Boolean(IN.parse) @waitingForLinkedIn = false + @sentMoreInfoEmail = false window.contractCallback = => @authorizedWithLinkedIn = IN?.User?.isAuthorized() @render() @@ -59,6 +61,7 @@ module.exports = class EmployerSignupView extends View context.userIsAuthorized = @authorizedWithLinkedIn context.userHasSignedContract = "employer" in me.get("permissions") context.userIsAnonymous = context.me.get('anonymous') + context.sentMoreInfoEmail = @sentMoreInfoEmail context 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}" 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) => window.tracker?.trackEvent 'Finished Employer Signup' + el = $("#signup-form") e.stopPropagation() - forms.clearFormAlerts(@$el) - userObject = forms.formToObject @$el + forms.clearFormAlerts(el) + userObject = forms.formToObject el delete userObject.subscribe for key, val of me.attributes when key in ["preferredLanguage", "testGroupNumber", "dateCreated", "wizardColor1", "name", "music", "volume", "emails"] userObject[key] ?= val userObject.emails ?= {} userObject.emails.employerNotes = {enabled: true} res = tv4.validateMultiple userObject, User.schema - return forms.applyErrorsToForm(@$el, res.errors) unless res.valid - @enableModalInProgress(@$el) + return forms.applyErrorsToForm(el, res.errors) unless res.valid + @enableModalInProgress(el) 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) -> window.location.hash = "employerSignupLoggingIn" From ff0d842871f99ec1d1ed90baa4662b516aba6c1a Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Fri, 13 Jun 2014 16:04:05 -0700 Subject: [PATCH 6/6] Better employer contact message. --- app/views/account/profile_view.coffee | 2 +- app/views/modal/job_profile_contact_modal.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/account/profile_view.coffee b/app/views/account/profile_view.coffee index ef49b583c..3cf4ab9f7 100644 --- a/app/views/account/profile_view.coffee +++ b/app/views/account/profile_view.coffee @@ -301,7 +301,7 @@ module.exports = class ProfileView extends View null onContactCandidate: (e) -> - @openModalView new JobProfileContactView recipientID: @user.id + @openModalView new JobProfileContactView recipientID: @user.id, recipientUserName: @user.get('name') showErrors: (errors) -> section = @$el.find '.saving' diff --git a/app/views/modal/job_profile_contact_modal.coffee b/app/views/modal/job_profile_contact_modal.coffee index ff968704f..4e35514af 100644 --- a/app/views/modal/job_profile_contact_modal.coffee +++ b/app/views/modal/job_profile_contact_modal.coffee @@ -36,7 +36,7 @@ module.exports = class JobProfileContactView extends ContactView contactMessage.recipientID = @options.recipientID res = tv4.validateMultiple contactMessage, contactSchema 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 sendContactMessage contactMessage, @$el $.post "/db/user/#{me.id}/track/contact_candidate"