mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-11-27 09:35:39 -05:00
Update teacher trial request views, some related bug fixes
* Tweak wording, options * Instead of storing changes in local storage, warn when users may lose changes by navigating away * Fix CreateTeacherAccountView so that if you connect to an existing account, the redirect is triggered * Fix users.coffee weird race condition
This commit is contained in:
parent
67686e8c71
commit
a452f1ce47
13 changed files with 205 additions and 111 deletions
|
@ -158,6 +158,12 @@ module.exports = class CocoRouter extends Backbone.Router
|
|||
return @routeDirectly('teachers/RestrictedToTeachersView')
|
||||
if options.studentsOnly and me.isTeacher()
|
||||
return @routeDirectly('courses/RestrictedToStudentsView')
|
||||
leavingMessage = _.result(window.currentView, 'onLeaveMessage')
|
||||
if leavingMessage
|
||||
if not confirm(leavingMessage)
|
||||
return @navigate(this.path, {replace: true})
|
||||
else
|
||||
window.currentView.onLeaveMessage = _.noop # to stop repeat confirm calls
|
||||
|
||||
path = 'play/CampaignView' if window.serverConfig.picoCTF and not /^(views)?\/?play/.test(path)
|
||||
path = "views/#{path}" if not _.string.startsWith(path, 'views/')
|
||||
|
@ -185,6 +191,7 @@ module.exports = class CocoRouter extends Backbone.Router
|
|||
@activateTab()
|
||||
view.afterInsert()
|
||||
view.didReappear()
|
||||
@path = document.location.pathname + document.location.search
|
||||
|
||||
closeCurrentView: ->
|
||||
if window.currentView?.reloadOnClose
|
||||
|
|
|
@ -159,4 +159,11 @@ window.serializeForIOS = serializeForIOS = (obj, depth=3) ->
|
|||
seen = null if root
|
||||
clone
|
||||
|
||||
window.onbeforeunload = (e) ->
|
||||
leavingMessage = _.result(window.currentView, 'onLeaveMessage')
|
||||
if leavingMessage
|
||||
return leavingMessage
|
||||
else
|
||||
return
|
||||
|
||||
$ -> init()
|
||||
|
|
|
@ -773,14 +773,20 @@
|
|||
email_exists: "User exists with this email."
|
||||
phone_number: "Phone number"
|
||||
phone_number_help: "Where can we reach you during the workday?"
|
||||
role_label: "Your role"
|
||||
role_help: "Select your primary role."
|
||||
primary_role_label: "Your Primary Role"
|
||||
role_default: "Select Role"
|
||||
primary_role_default: "Select Primary Role"
|
||||
purchaser_role_default: "Select Purchaser Role"
|
||||
tech_coordinator: "Technology coordinator"
|
||||
advisor: "Advisor"
|
||||
principal: "Principal"
|
||||
superintendent: "Superintendent"
|
||||
parent: "Parent"
|
||||
purchaser_role_label: "Your Purchaser Role"
|
||||
influence_advocate: "Influence/Advocate"
|
||||
evaluate_recommend: "Evaluate/Recommend"
|
||||
approve_funds: "Approve Funds"
|
||||
no_purchaser_role: "No role in purchase decisions"
|
||||
organization_label: "Name of School/District"
|
||||
city: "City"
|
||||
state: "State"
|
||||
|
@ -801,6 +807,7 @@
|
|||
finish_signup: "Finish creating your teacher account:"
|
||||
finish_signup_p: "Create an account to set up a class, add your students, and monitor their progress as they learn computer science."
|
||||
signup_with: "Sign up with:"
|
||||
connect_with: "Connect with:"
|
||||
conversion_warning: "WARNING: Your current account is a <em>Student Account</em>. Once you submit this form, your account will be updated to a Teacher Account."
|
||||
learn_more_modal: "Teacher accounts on CodeCombat have the ability to monitor student progress, assign enrollments and manage classrooms. Teacher accounts cannot be a part of a classroom - if you are currently enrolled in a class using this account, you will no longer be able to access it once you update to a Teacher Account."
|
||||
create_account: "Create a Teacher Account"
|
||||
|
@ -1021,7 +1028,8 @@
|
|||
buy_course1: "Buy this course"
|
||||
select_all_courses: "Select 'All Courses' for a 50% discount!"
|
||||
all_courses: "All Courses"
|
||||
number_students: "Number of students"
|
||||
number_programming_students: "Number of Programming Students"
|
||||
number_total_students: "Total Students in School/District"
|
||||
enter_number_students: "Enter the number of students you need for this class."
|
||||
name_class: "Name your class"
|
||||
displayed_course_page: "This will be displayed on the course page for you and your students. It can be changed later."
|
||||
|
|
|
@ -59,7 +59,7 @@ block content
|
|||
|
||||
.col-md-4.col-sm-6
|
||||
.form-group
|
||||
label.control-label(data-i18n="teachers_quote.role_label")
|
||||
label.control-label(data-i18n="teachers_quote.primary_role_label")
|
||||
.help-block.small
|
||||
em.text-info(data-i18n="teachers_quote.role_help")
|
||||
select.form-control(name="role")
|
||||
|
@ -98,7 +98,7 @@ block content
|
|||
.row.m-y-2
|
||||
.col-md-offset-2.col-md-4
|
||||
.form-group
|
||||
label.control-label(data-i18n="courses.number_students")
|
||||
label.control-label(data-i18n="courses.number_programming_students")
|
||||
.help-block.small
|
||||
em.text-info(data-i18n="teachers_quote.num_students_help")
|
||||
select.form-control(name="numStudents")
|
||||
|
@ -111,6 +111,19 @@ block content
|
|||
option 501-1000
|
||||
option 1000+
|
||||
|
||||
.col-md-4.col-sm-6
|
||||
.form-group
|
||||
label.control-label
|
||||
span(data-i18n="courses.number_total_students")
|
||||
span.spl.text-muted(data-i18n="signup.optional")
|
||||
select.form-control(name="numStudentsTotal")
|
||||
option(data-i18n="teachers_quote.num_students_default", value='')
|
||||
option 1-500
|
||||
option 500-1,000
|
||||
option 1,000-5,000
|
||||
option 5,000-10,000
|
||||
option 10,000+
|
||||
|
||||
.form-group
|
||||
.row.m-y-2
|
||||
.col-md-offset-2.col-md-10
|
||||
|
|
|
@ -12,12 +12,12 @@ block content
|
|||
a.login-link(data-i18n="login.log_in")
|
||||
#social-network-signups.m-y-2
|
||||
button#facebook-signup-btn.btn.btn-facebook.btn-lg.m-x-1
|
||||
span.spr(data-i18n="teachers_quote.signup_with")
|
||||
span.spr(data-i18n="teachers_quote.connect_with")
|
||||
| Facebook
|
||||
img.m-l-1(src='/images/pages/community/logo_facebook.png')
|
||||
|
||||
button#gplus-signup-btn.btn.btn-gplus.btn-lg.spr
|
||||
span.spr(data-i18n="teachers_quote.signup_with")
|
||||
span.spr(data-i18n="teachers_quote.connect_with")
|
||||
| G+
|
||||
img.m-l-1(src='/images/pages/community/logo_g+.png')
|
||||
|
||||
|
@ -76,9 +76,7 @@ block content
|
|||
|
||||
.col-md-4.col-sm-6
|
||||
.form-group
|
||||
label.control-label(data-i18n="teachers_quote.role_label")
|
||||
.help-block.small
|
||||
em.text-info(data-i18n="teachers_quote.role_help")
|
||||
label.control-label(data-i18n="teachers_quote.primary_role_label")
|
||||
select.form-control(name="role")
|
||||
option(data-i18n="teachers_quote.role_default", , value='')
|
||||
option(data-i18n="courses.teacher", value="Teacher")
|
||||
|
@ -115,7 +113,7 @@ block content
|
|||
.row.m-y-2
|
||||
.col-md-offset-2.col-md-4
|
||||
.form-group
|
||||
label.control-label(data-i18n="courses.number_students")
|
||||
label.control-label(data-i18n="courses.number_programming_students")
|
||||
.help-block.small
|
||||
em.text-info(data-i18n="teachers_quote.num_students_help")
|
||||
select.form-control(name="numStudents")
|
||||
|
@ -128,6 +126,19 @@ block content
|
|||
option 501-1000
|
||||
option 1000+
|
||||
|
||||
.col-md-4.col-sm-6
|
||||
.form-group
|
||||
label.control-label
|
||||
span(data-i18n="courses.number_total_students")
|
||||
span.spl.text-muted(data-i18n="signup.optional")
|
||||
select.form-control(name="numStudentsTotal")
|
||||
option(data-i18n="teachers_quote.num_students_default", value='')
|
||||
option 1-500
|
||||
option 500-1,000
|
||||
option 1,000-5,000
|
||||
option 5,000-10,000
|
||||
option 10,000+
|
||||
|
||||
.form-group
|
||||
|
||||
.row.m-y-2
|
||||
|
|
|
@ -36,13 +36,15 @@ block content
|
|||
.row
|
||||
.col-md-offset-2.col-md-4.col-sm-6
|
||||
.form-group
|
||||
label.control-label(data-i18n="general.username")
|
||||
label.control-label
|
||||
span(data-i18n="general.username")
|
||||
span.spl.text-muted(data-i18n="signup.optional")
|
||||
- var name = me.get('name') || '';
|
||||
input.form-control(name="name" value=name, disabled=!!name)
|
||||
|
||||
.col-md-4.col-sm-6
|
||||
.form-group
|
||||
label.control-label(data-i18n="general.email")
|
||||
label.control-label(data-i18n="share_progress_modal.form_label")
|
||||
- var email = me.get('email') || '';
|
||||
input.form-control(name="email" value=email, disabled=!!email)
|
||||
|
||||
|
@ -59,6 +61,16 @@ block content
|
|||
- var lastName = me.get('lastName') || '';
|
||||
input.form-control(name="lastName" value=lastName, disabled=!!lastName)
|
||||
|
||||
if !me.isAnonymous()
|
||||
.row
|
||||
.col-md-offset-2.col-md-4.col-sm-6
|
||||
.form-group
|
||||
label.control-label
|
||||
span(data-i18n="teachers_quote.phone_number")
|
||||
.help-block.small
|
||||
em.text-info(data-i18n="teachers_quote.phone_number_help")
|
||||
input.form-control(name="phoneNumber")
|
||||
|
||||
if me.isAnonymous()
|
||||
.row
|
||||
.col-md-offset-2.col-md-4.col-sm-6
|
||||
|
@ -67,30 +79,38 @@ block content
|
|||
- var email = me.get('email') || '';
|
||||
input.form-control(name="email" type="email", value=email, disabled=!!email)
|
||||
|
||||
.col-md-4.col-sm-6
|
||||
.form-group
|
||||
label.control-label
|
||||
span(data-i18n="teachers_quote.phone_number")
|
||||
.help-block.small
|
||||
em.text-info(data-i18n="teachers_quote.phone_number_help")
|
||||
input.form-control(name="phoneNumber")
|
||||
|
||||
|
||||
.row
|
||||
.col-md-offset-2.col-md-4.col-sm-6
|
||||
.form-group
|
||||
label.control-label
|
||||
span(data-i18n="teachers_quote.phone_number")
|
||||
span.spl.text-muted(data-i18n="signup.optional")
|
||||
.help-block.small
|
||||
em.text-info(data-i18n="teachers_quote.phone_number_help")
|
||||
input.form-control(name="phoneNumber")
|
||||
|
||||
.col-md-4.col-sm-6
|
||||
.form-group
|
||||
label.control-label(data-i18n="teachers_quote.role_label")
|
||||
.help-block.small
|
||||
em.text-info(data-i18n="teachers_quote.role_help")
|
||||
label.control-label(data-i18n="teachers_quote.primary_role_label")
|
||||
select.form-control(name="role")
|
||||
option(data-i18n="teachers_quote.role_default", , value='')
|
||||
option(data-i18n="teachers_quote.primary_role_default", , value='')
|
||||
option(data-i18n="courses.teacher", value="Teacher")
|
||||
option(data-i18n="teachers_quote.tech_coordinator", value="Technology coordinator")
|
||||
option(data-i18n="teachers_quote.advisor", value="Advisor")
|
||||
option(data-i18n="teachers_quote.principal", value="Principal")
|
||||
option(data-i18n="teachers_quote.superintendent", value="Superintendent")
|
||||
option(data-i18n="teachers_quote.parent", value="Parent")
|
||||
|
||||
|
||||
.col-md-4.col-sm-6
|
||||
.form-group
|
||||
label.control-label(data-i18n="teachers_quote.purchaser_role_label")
|
||||
select.form-control(name="purchaserRole")
|
||||
option(data-i18n="teachers_quote.purchaser_role_default", , value='')
|
||||
option(data-i18n="teachers_quote.influence_advocate", value="Influence/Advocate")
|
||||
option(data-i18n="teachers_quote.evaluate_recommend", value="Evaluate/Recommend")
|
||||
option(data-i18n="teachers_quote.approve_funds", value="Approve Funds")
|
||||
option(data-i18n="teachers_quote.no_purchaser_role", value="No role in purchase decisions")
|
||||
|
||||
#form-school-info
|
||||
.row
|
||||
.col-md-offset-2.col-md-4.col-sm-6
|
||||
|
@ -118,7 +138,7 @@ block content
|
|||
.row
|
||||
.col-md-offset-2.col-md-4
|
||||
.form-group
|
||||
label.control-label(data-i18n="courses.number_students")
|
||||
label.control-label(data-i18n="courses.number_programming_students")
|
||||
.help-block.small
|
||||
em.text-info(data-i18n="teachers_quote.num_students_help")
|
||||
select.form-control(name="numStudents")
|
||||
|
@ -130,6 +150,17 @@ block content
|
|||
option 201-500
|
||||
option 501-1000
|
||||
option 1000+
|
||||
|
||||
.col-md-4.col-sm-6
|
||||
.form-group
|
||||
label.control-label(data-i18n="courses.number_total_students")
|
||||
select.form-control(name="numStudentsTotal")
|
||||
option(data-i18n="teachers_quote.num_students_default", value='')
|
||||
option 1-500
|
||||
option 500-1,000
|
||||
option 1,000-5,000
|
||||
option 5,000-10,000
|
||||
option 10,000+
|
||||
|
||||
.form-group
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ forms = require 'core/forms'
|
|||
TrialRequest = require 'models/TrialRequest'
|
||||
TrialRequests = require 'collections/TrialRequests'
|
||||
AuthModal = require 'views/core/AuthModal'
|
||||
storage = require 'core/storage'
|
||||
errors = require 'core/errors'
|
||||
User = require 'models/User'
|
||||
ConfirmModal = require 'views/editor/modal/ConfirmModal'
|
||||
|
@ -35,6 +34,10 @@ module.exports = class ConvertToTeacherAccountView extends RootView
|
|||
@trialRequests.fetchOwn()
|
||||
@supermodel.trackCollection(@trialRequests)
|
||||
|
||||
onLeaveMessage: ->
|
||||
if @formChanged
|
||||
return 'Your account has not been updated! If you continue, your changes will be lost.'
|
||||
|
||||
invalidateNCES: ->
|
||||
for key in NCES_KEYS
|
||||
@$('input[name="nces_' + key + '"]').val ''
|
||||
|
@ -58,13 +61,6 @@ module.exports = class ConvertToTeacherAccountView extends RootView
|
|||
@$('#other-education-level-checkbox').attr('checked', !!otherLevel)
|
||||
@$('#other-education-level-input').val(otherLevel)
|
||||
|
||||
# apply changes from local storage
|
||||
obj = storage.load(FORM_KEY)
|
||||
if obj
|
||||
@$('#other-education-level-checkbox').attr('checked', obj.otherChecked)
|
||||
@$('#other-education-level-input').val(obj.otherInput)
|
||||
forms.objectToForm(@$('form'), obj, { overwriteExisting: true })
|
||||
|
||||
$("#organization-control").autocomplete({hint: false}, [
|
||||
source: (query, callback) ->
|
||||
algolia.schoolsIndex.search(query, { hitsPerPage: 5, aroundLatLngViaIP: false }).then (answer) ->
|
||||
|
@ -88,14 +84,10 @@ module.exports = class ConvertToTeacherAccountView extends RootView
|
|||
for key in NCES_KEYS
|
||||
@$('input[name="nces_' + key + '"]').val suggestion[key]
|
||||
|
||||
@onChangeRequestForm()
|
||||
@onChangeForm()
|
||||
|
||||
onChangeRequestForm: ->
|
||||
# save changes to local storage
|
||||
obj = forms.formToObject(@$('form'))
|
||||
obj.otherChecked = @$('#other-education-level-checkbox').is(':checked')
|
||||
obj.otherInput = @$('#other-education-level-input').val()
|
||||
storage.save(FORM_KEY, obj, 10)
|
||||
onChangeForm: ->
|
||||
@formChanged = true
|
||||
|
||||
onSubmitForm: (e) ->
|
||||
e.preventDefault()
|
||||
|
@ -149,13 +141,15 @@ module.exports = class ConvertToTeacherAccountView extends RootView
|
|||
errors.showNotyNetworkError(arguments...)
|
||||
|
||||
onTrialRequestSubmit: ->
|
||||
@formChanged = false
|
||||
me.setRole @trialRequest.get('properties').role.toLowerCase(), true
|
||||
storage.remove(FORM_KEY)
|
||||
application.router.navigate('/teachers/classes', {trigger: true})
|
||||
|
||||
formSchema = {
|
||||
type: 'object'
|
||||
required: ['firstName', 'lastName', 'organization', 'role', 'numStudents']
|
||||
required: [
|
||||
'firstName', 'lastName', 'organization', 'role', 'numStudents', 'city', 'state', 'country'
|
||||
]
|
||||
properties:
|
||||
firstName: { type: 'string' }
|
||||
lastName: { type: 'string' }
|
||||
|
@ -166,6 +160,7 @@ formSchema = {
|
|||
state: { type: 'string' }
|
||||
country: { type: 'string' }
|
||||
numStudents: { type: 'string' }
|
||||
numStudentsTotal: { type: 'string' }
|
||||
educationLevel: {
|
||||
type: 'array'
|
||||
items: { type: 'string' }
|
||||
|
|
|
@ -3,7 +3,6 @@ forms = require 'core/forms'
|
|||
TrialRequest = require 'models/TrialRequest'
|
||||
TrialRequests = require 'collections/TrialRequests'
|
||||
AuthModal = require 'views/core/AuthModal'
|
||||
storage = require 'core/storage'
|
||||
errors = require 'core/errors'
|
||||
User = require 'models/User'
|
||||
algolia = require 'core/services/algolia'
|
||||
|
@ -33,6 +32,10 @@ module.exports = class CreateTeacherAccountView extends RootView
|
|||
@trialRequests.fetchOwn()
|
||||
@supermodel.trackCollection(@trialRequests)
|
||||
|
||||
onLeaveMessage: ->
|
||||
if @formChanged
|
||||
return 'Your account has not been created! If you continue, your changes will be lost.'
|
||||
|
||||
onLoaded: ->
|
||||
if @trialRequests.size()
|
||||
@trialRequest = @trialRequests.first()
|
||||
|
@ -57,13 +60,6 @@ module.exports = class CreateTeacherAccountView extends RootView
|
|||
@$('#other-education-level-checkbox').attr('checked', !!otherLevel)
|
||||
@$('#other-education-level-input').val(otherLevel)
|
||||
|
||||
# apply changes from local storage
|
||||
obj = storage.load(FORM_KEY)
|
||||
if obj
|
||||
@$('#other-education-level-checkbox').attr('checked', obj.otherChecked)
|
||||
@$('#other-education-level-input').val(obj.otherInput)
|
||||
forms.objectToForm(@$('form'), obj, { overwriteExisting: true })
|
||||
|
||||
$("#organization-control").autocomplete({hint: false}, [
|
||||
source: (query, callback) ->
|
||||
algolia.schoolsIndex.search(query, { hitsPerPage: 5, aroundLatLngViaIP: false }).then (answer) ->
|
||||
|
@ -87,20 +83,14 @@ module.exports = class CreateTeacherAccountView extends RootView
|
|||
for key in NCES_KEYS
|
||||
@$('input[name="nces_' + key + '"]').val suggestion[key]
|
||||
|
||||
@onChangeRequestForm()
|
||||
@onChangeForm()
|
||||
|
||||
onClickLoginLink: ->
|
||||
modal = new AuthModal({ initialValues: { email: @trialRequest.get('properties')?.email } })
|
||||
@openModalView(modal)
|
||||
|
||||
onChangeRequestForm: ->
|
||||
# Local storage is being used to store the contents of the form whenever it changes,
|
||||
# and filling in the stored values if the page is reloaded or navigated away from and returned to.
|
||||
# save changes to local storage
|
||||
obj = forms.formToObject(@$('form'))
|
||||
obj.otherChecked = @$('#other-education-level-checkbox').is(':checked')
|
||||
obj.otherInput = @$('#other-education-level-input').val()
|
||||
storage.save(FORM_KEY, obj, 10)
|
||||
onChangeForm: ->
|
||||
@formChanged = true
|
||||
|
||||
onSubmitForm: (e) ->
|
||||
e.preventDefault()
|
||||
|
@ -168,7 +158,7 @@ module.exports = class CreateTeacherAccountView extends RootView
|
|||
@openModalView(modal)
|
||||
|
||||
onTrialRequestSubmit: ->
|
||||
storage.remove(FORM_KEY)
|
||||
@formChanged = false
|
||||
attrs = _.pick(forms.formToObject(@$('form')), 'name', 'email', 'role')
|
||||
attrs.role = attrs.role.toLowerCase()
|
||||
options = {}
|
||||
|
@ -215,7 +205,7 @@ module.exports = class CreateTeacherAccountView extends RootView
|
|||
success: =>
|
||||
me.loginGPlusUser(@gplusAttrs.gplusID, {
|
||||
success: ->
|
||||
application.router.navigate('/teachers/update-account')
|
||||
application.router.navigate('/teachers/update-account', {trigger: true})
|
||||
error: errors.showNotyNetworkError
|
||||
})
|
||||
})
|
||||
|
@ -224,6 +214,7 @@ module.exports = class CreateTeacherAccountView extends RootView
|
|||
})
|
||||
|
||||
onGPlusConnected: ->
|
||||
@formChanged = true
|
||||
forms.objectToForm(@$('form'), @gplusAttrs)
|
||||
for field in ['email', 'firstName', 'lastName']
|
||||
input = @$("input[name='#{field}']")
|
||||
|
@ -257,7 +248,7 @@ module.exports = class CreateTeacherAccountView extends RootView
|
|||
success: =>
|
||||
me.loginFacebookUser(@facebookAttrs.facebookID, {
|
||||
success: ->
|
||||
application.router.navigate('/teachers/update-account')
|
||||
application.router.navigate('/teachers/update-account', {trigger: true})
|
||||
error: errors.showNotyNetworkError
|
||||
})
|
||||
})
|
||||
|
@ -266,6 +257,7 @@ module.exports = class CreateTeacherAccountView extends RootView
|
|||
})
|
||||
|
||||
onFacebookConnected: ->
|
||||
@formChanged = true
|
||||
forms.objectToForm(@$('form'), @facebookAttrs)
|
||||
for field in ['email', 'firstName', 'lastName']
|
||||
input = @$("input[name='#{field}']")
|
||||
|
@ -278,7 +270,10 @@ module.exports = class CreateTeacherAccountView extends RootView
|
|||
|
||||
formSchema = {
|
||||
type: 'object'
|
||||
required: ['firstName', 'lastName', 'email', 'organization', 'role', 'numStudents']
|
||||
required: [
|
||||
'firstName', 'lastName', 'email', 'organization', 'role', 'numStudents', 'city'
|
||||
'state', 'country'
|
||||
]
|
||||
properties:
|
||||
password1: { type: 'string' }
|
||||
password2: { type: 'string' }
|
||||
|
@ -293,6 +288,7 @@ formSchema = {
|
|||
state: { type: 'string' }
|
||||
country: { type: 'string' }
|
||||
numStudents: { type: 'string' }
|
||||
numStudentsTotal: { type: 'string' }
|
||||
educationLevel: {
|
||||
type: 'array'
|
||||
items: { type: 'string' }
|
||||
|
|
|
@ -3,12 +3,10 @@ forms = require 'core/forms'
|
|||
TrialRequest = require 'models/TrialRequest'
|
||||
TrialRequests = require 'collections/TrialRequests'
|
||||
AuthModal = require 'views/core/AuthModal'
|
||||
storage = require 'core/storage'
|
||||
errors = require 'core/errors'
|
||||
ConfirmModal = require 'views/editor/modal/ConfirmModal'
|
||||
algolia = require 'core/services/algolia'
|
||||
|
||||
FORM_KEY = 'request-quote-form'
|
||||
SIGNUP_REDIRECT = '/teachers'
|
||||
NCES_KEYS = ['id', 'name', 'district', 'district_id', 'district_schools', 'district_students', 'students', 'phone']
|
||||
|
||||
|
@ -35,6 +33,11 @@ module.exports = class RequestQuoteView extends RootView
|
|||
@trialRequests = new TrialRequests()
|
||||
@trialRequests.fetchOwn()
|
||||
@supermodel.trackCollection(@trialRequests)
|
||||
@formChanged = false
|
||||
|
||||
onLeaveMessage: ->
|
||||
if @formChanged
|
||||
return 'Your request has not been submitted! If you continue, your changes will be lost.'
|
||||
|
||||
onLoaded: ->
|
||||
if @trialRequests.size()
|
||||
|
@ -60,13 +63,6 @@ module.exports = class RequestQuoteView extends RootView
|
|||
@$('#other-education-level-checkbox').attr('checked', !!otherLevel)
|
||||
@$('#other-education-level-input').val(otherLevel)
|
||||
|
||||
# apply changes from local storage
|
||||
obj = storage.load(FORM_KEY)
|
||||
if obj
|
||||
@$('#other-education-level-checkbox').attr('checked', obj.otherChecked)
|
||||
@$('#other-education-level-input').val(obj.otherInput)
|
||||
forms.objectToForm(@$('#request-form'), obj, { overwriteExisting: true })
|
||||
|
||||
$("#organization-control").autocomplete({hint: false}, [
|
||||
source: (query, callback) ->
|
||||
algolia.schoolsIndex.search(query, { hitsPerPage: 5, aroundLatLngViaIP: false }).then (answer) ->
|
||||
|
@ -93,18 +89,14 @@ module.exports = class RequestQuoteView extends RootView
|
|||
@onChangeRequestForm()
|
||||
|
||||
onChangeRequestForm: ->
|
||||
# save changes to local storage
|
||||
obj = forms.formToObject(@$('form'))
|
||||
obj.otherChecked = @$('#other-education-level-checkbox').is(':checked')
|
||||
obj.otherInput = @$('#other-education-level-input').val()
|
||||
storage.save(FORM_KEY, obj, 10)
|
||||
@formChanged = true
|
||||
|
||||
onSubmitRequestForm: (e) ->
|
||||
e.preventDefault()
|
||||
form = @$('#request-form')
|
||||
attrs = forms.formToObject(form)
|
||||
|
||||
# custom other input logic (also used in form local storage save/restore)
|
||||
# custom other input logic
|
||||
if @$('#other-education-level-checkbox').is(':checked')
|
||||
val = @$('#other-education-level-input').val()
|
||||
attrs.educationLevel.push(val) if val
|
||||
|
@ -138,7 +130,10 @@ module.exports = class RequestQuoteView extends RootView
|
|||
decline: $.i18n.t('common.cancel')
|
||||
})
|
||||
@openModalView(modal)
|
||||
modal.once 'confirm', @saveTrialRequest, @
|
||||
modal.once('confirm', (->
|
||||
modal.hide()
|
||||
@saveTrialRequest()
|
||||
), @)
|
||||
else
|
||||
@saveTrialRequest()
|
||||
|
||||
|
@ -166,10 +161,10 @@ module.exports = class RequestQuoteView extends RootView
|
|||
@openModalView(modal)
|
||||
|
||||
onTrialRequestSubmit: ->
|
||||
@formChanged = false
|
||||
me.setRole @trialRequest.get('properties').role.toLowerCase(), true
|
||||
defaultName = [@trialRequest.get('firstName'), @trialRequest.get('lastName')].join(' ')
|
||||
@$('input[name="name"]').val(defaultName)
|
||||
storage.remove(FORM_KEY)
|
||||
@$('#request-form, #form-submit-success').toggleClass('hide')
|
||||
@scrollToTop(0)
|
||||
$('#flying-focus').css({top: 0, left: 0}) # Hack copied from Router.coffee#187. Ideally we'd swap out the view and have view-swapping logic handle this
|
||||
|
@ -264,19 +259,23 @@ module.exports = class RequestQuoteView extends RootView
|
|||
|
||||
requestFormSchemaAnonymous = {
|
||||
type: 'object'
|
||||
required: ['firstName', 'lastName', 'email', 'organization', 'role', 'numStudents']
|
||||
required: [
|
||||
'firstName', 'lastName', 'email', 'organization', 'role', 'purchaserRole', 'numStudents',
|
||||
'numStudentsTotal', 'phoneNumber', 'city', 'state', 'country']
|
||||
properties:
|
||||
firstName: { type: 'string' }
|
||||
lastName: { type: 'string' }
|
||||
name: { type: 'string', minLength: 1 }
|
||||
name: { type: 'string' }
|
||||
email: { type: 'string', format: 'email' }
|
||||
phoneNumber: { type: 'string' }
|
||||
role: { type: 'string' }
|
||||
purchaserRole: { type: 'string' }
|
||||
organization: { type: 'string' }
|
||||
city: { type: 'string' }
|
||||
state: { type: 'string' }
|
||||
country: { type: 'string' }
|
||||
numStudents: { type: 'string' }
|
||||
numStudentsTotal: { type: 'string' }
|
||||
educationLevel: {
|
||||
type: 'array'
|
||||
items: { type: 'string' }
|
||||
|
|
|
@ -14,7 +14,7 @@ module.exports =
|
|||
fetchByGPlusID: wrap (req, res, next) ->
|
||||
gpID = req.query.gplusID
|
||||
gpAT = req.query.gplusAccessToken
|
||||
next() unless gpID and gpAT
|
||||
return next() unless gpID and gpAT
|
||||
|
||||
dbq = User.find()
|
||||
dbq.select(parse.getProjectFromReq(req))
|
||||
|
@ -29,7 +29,7 @@ module.exports =
|
|||
fetchByFacebookID: wrap (req, res, next) ->
|
||||
fbID = req.query.facebookID
|
||||
fbAT = req.query.facebookAccessToken
|
||||
next() unless fbID and fbAT
|
||||
return next() unless fbID and fbAT
|
||||
|
||||
dbq = User.find()
|
||||
dbq.select(parse.getProjectFromReq(req))
|
||||
|
|
|
@ -59,7 +59,18 @@ describe 'ConvertToTeacherAccountView (/teachers/update-account)', ->
|
|||
afterEach (done) ->
|
||||
_.defer(done) # let everything finish loading, keep navigate spied on
|
||||
|
||||
|
||||
describe 'when the form is unchanged', ->
|
||||
it 'does not prevent navigating away', ->
|
||||
expect(_.result(view, 'onLeaveMessage')).toBeFalsy()
|
||||
|
||||
describe 'when the form has changed but is not submitted', ->
|
||||
beforeEach ->
|
||||
view.$el.find('form').trigger('change')
|
||||
|
||||
it 'prevents navigating away', ->
|
||||
expect(_.result(view, 'onLeaveMessage')).toBeTruthy()
|
||||
|
||||
|
||||
describe 'when the user already has a TrialRequest and is a teacher', ->
|
||||
beforeEach (done) ->
|
||||
spyOn(me, 'isTeacher').and.returnValue(true)
|
||||
|
@ -121,11 +132,15 @@ describe 'ConvertToTeacherAccountView (/teachers/update-account)', ->
|
|||
|
||||
describe 'submitting the form', ->
|
||||
beforeEach ->
|
||||
view.$el.find('#request-form').trigger('change') # to confirm navigating away isn't prevented
|
||||
_.last(view.trialRequests.fakeRequests).respondWith({ status: 200, responseText: JSON.stringify('[]') })
|
||||
form = view.$('form')
|
||||
forms.objectToForm(form, successForm, {overwriteExisting: true})
|
||||
form.submit()
|
||||
|
||||
it 'does not prevent navigating away', ->
|
||||
expect(_.result(view, 'onLeaveMessage')).toBeFalsy()
|
||||
|
||||
it 'creates a new TrialRequest with the information', ->
|
||||
request = _.last(view.trialRequest.fakeRequests)
|
||||
expect(request).toBeTruthy()
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
CreateTeacherAccountView = require 'views/teachers/CreateTeacherAccountView'
|
||||
storage = require 'core/storage'
|
||||
forms = require 'core/forms'
|
||||
|
||||
describe '/teachers/signup', ->
|
||||
|
@ -55,8 +54,6 @@ describe 'CreateTeacherAccountView', ->
|
|||
view.render()
|
||||
jasmine.demoEl(view.$el)
|
||||
|
||||
spyOn(storage, 'load').and.returnValue({ lastName: 'Saved Changes' })
|
||||
|
||||
request = jasmine.Ajax.requests.mostRecent()
|
||||
request.respondWith({
|
||||
status: 200
|
||||
|
@ -70,6 +67,17 @@ describe 'CreateTeacherAccountView', ->
|
|||
})
|
||||
_.defer done # Let SuperModel finish
|
||||
|
||||
describe 'when the form is unchanged', ->
|
||||
it 'does not prevent navigating away', ->
|
||||
expect(_.result(view, 'onLeaveMessage')).toBeFalsy()
|
||||
|
||||
describe 'when the form has changed but is not submitted', ->
|
||||
beforeEach ->
|
||||
view.$el.find('form').trigger('change')
|
||||
|
||||
it 'prevents navigating away', ->
|
||||
expect(_.result(view, 'onLeaveMessage')).toBeTruthy()
|
||||
|
||||
describe '"Log in" link', ->
|
||||
|
||||
it 'opens the log in modal', ->
|
||||
|
@ -100,7 +108,7 @@ describe 'CreateTeacherAccountView', ->
|
|||
request = jasmine.Ajax.requests.mostRecent()
|
||||
expect(request.url).toBe('/auth/login-facebook')
|
||||
|
||||
describe 'when the user\s info is loaded', ->
|
||||
describe 'when the user connects with Facebook and there isn\'t already an associated account', ->
|
||||
beforeEach ->
|
||||
request = jasmine.Ajax.requests.mostRecent()
|
||||
request.respondWith({ status: 404, responseText: '{}' })
|
||||
|
@ -156,7 +164,7 @@ describe 'CreateTeacherAccountView', ->
|
|||
request = jasmine.Ajax.requests.mostRecent()
|
||||
expect(request.url).toBe('/auth/login-gplus')
|
||||
|
||||
describe 'when the user\s info is loaded', ->
|
||||
describe 'when the user connects with F+ and there isn\'t already an associated account', ->
|
||||
beforeEach ->
|
||||
request = jasmine.Ajax.requests.mostRecent()
|
||||
request.respondWith({ status: 404, responseText: '{}' })
|
||||
|
@ -194,9 +202,13 @@ describe 'CreateTeacherAccountView', ->
|
|||
describe 'submitting the form successfully', ->
|
||||
|
||||
beforeEach ->
|
||||
view.$el.find('#request-form').trigger('change') # to confirm navigating away isn't prevented
|
||||
form = view.$('form')
|
||||
forms.objectToForm(form, successForm)
|
||||
form.submit()
|
||||
|
||||
it 'does not prevent navigating away', ->
|
||||
expect(_.result(view, 'onLeaveMessage')).toBeFalsy()
|
||||
|
||||
it 'submits a trial request, which does not include "account" settings', ->
|
||||
request = jasmine.Ajax.requests.mostRecent()
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
RequestQuoteView = require 'views/teachers/RequestQuoteView'
|
||||
storage = require 'core/storage'
|
||||
forms = require 'core/forms'
|
||||
|
||||
describe 'RequestQuoteView', ->
|
||||
|
@ -17,11 +16,13 @@ describe 'RequestQuoteView', ->
|
|||
state: 'AA'
|
||||
country: 'asdf'
|
||||
numStudents: '1-10'
|
||||
numStudentsTotal: '10,000+'
|
||||
purchaserRole: 'Approve Funds'
|
||||
educationLevel: ['Middle']
|
||||
}
|
||||
|
||||
isSubmitRequest = (r) -> _.string.startsWith(r.url, '/db/trial.request') and r.method is 'POST'
|
||||
|
||||
|
||||
describe 'when user is anonymous and has an existing trial request', ->
|
||||
beforeEach (done) ->
|
||||
me.clear()
|
||||
|
@ -60,8 +61,6 @@ describe 'RequestQuoteView', ->
|
|||
view.render()
|
||||
jasmine.demoEl(view.$el)
|
||||
|
||||
spyOn(storage, 'load').and.returnValue({ lastName: 'Saved Changes' })
|
||||
|
||||
request = jasmine.Ajax.requests.mostRecent()
|
||||
request.respondWith({
|
||||
status: 200
|
||||
|
@ -78,18 +77,6 @@ describe 'RequestQuoteView', ->
|
|||
it 'shows form with data from the most recent request', ->
|
||||
expect(view.$('input[name="firstName"]').val()).toBe('First')
|
||||
|
||||
it 'prioritizes showing local, unsaved changes', ->
|
||||
expect(view.$('input[name="lastName"]').val()).toBe('Saved Changes')
|
||||
|
||||
describe 'when the form changes', ->
|
||||
|
||||
it 'stores local, unsaved changes', ->
|
||||
spyOn(storage, 'save')
|
||||
view.$('input[name="firstName"]').val('Just Changed').change()
|
||||
expect(storage.save).toHaveBeenCalled()
|
||||
args = storage.save.calls.argsFor(0)
|
||||
expect(args[1].firstName).toBe('Just Changed')
|
||||
|
||||
describe 'when a user is anonymous and does NOT have an existing trial request', ->
|
||||
beforeEach (done) ->
|
||||
me.clear()
|
||||
|
@ -100,8 +87,6 @@ describe 'RequestQuoteView', ->
|
|||
view.render()
|
||||
jasmine.demoEl(view.$el)
|
||||
|
||||
spyOn(storage, 'load').and.returnValue({ lastName: 'Saved Changes' })
|
||||
|
||||
request = jasmine.Ajax.requests.mostRecent()
|
||||
request.respondWith({
|
||||
status: 200
|
||||
|
@ -109,8 +94,20 @@ describe 'RequestQuoteView', ->
|
|||
})
|
||||
_.defer done # Let SuperModel finish
|
||||
|
||||
describe 'when the form is unchanged', ->
|
||||
it 'does not prevent navigating away', ->
|
||||
expect(_.result(view, 'onLeaveMessage')).toBeFalsy()
|
||||
|
||||
describe 'when the form has changed but is not submitted', ->
|
||||
beforeEach ->
|
||||
view.$el.find('#request-form').trigger('change')
|
||||
|
||||
it 'prevents navigating away', ->
|
||||
expect(_.result(view, 'onLeaveMessage')).toBeTruthy()
|
||||
|
||||
describe 'on successful form submit', ->
|
||||
beforeEach ->
|
||||
view.$el.find('#request-form').trigger('change') # to confirm navigating away isn't prevented
|
||||
forms.objectToForm(view.$el, successFormValues)
|
||||
view.$('#request-form').submit()
|
||||
@submitRequest = _.last(jasmine.Ajax.requests.filter(isSubmitRequest))
|
||||
|
@ -119,6 +116,9 @@ describe 'RequestQuoteView', ->
|
|||
responseText: JSON.stringify(_.extend({_id: 'a'}, successFormValues))
|
||||
})
|
||||
|
||||
it 'does not prevent navigating away', ->
|
||||
expect(_.result(view, 'onLeaveMessage')).toBeFalsy()
|
||||
|
||||
it 'creates a new trial request', ->
|
||||
expect(@submitRequest).toBeTruthy()
|
||||
expect(@submitRequest.method).toBe('POST')
|
||||
|
|
Loading…
Reference in a new issue