Add required district field to teacher trial request forms

School now optional
N/A placeholder on district field and don't save it if it's n/a
Updating required field error UI a bit, and there is some larger
refactoring needed here later.
NCES phone number for district only entries will currently be a child
school, will fix later.

Closes #3818
This commit is contained in:
Matt Lott 2016-08-05 09:08:37 -07:00
parent f4c0e4144e
commit 6f08d5e422
18 changed files with 630 additions and 426 deletions

View file

@ -45,7 +45,7 @@ module.exports.applyErrorsToForm = (el, errors, warning=false) ->
for error in errors
if error.code is tv4.errorCodes.OBJECT_REQUIRED
prop = _.last(_.string.words(error.message)) # hack
message = 'Required field'
message = $.i18n.t('common.required_field')
else if error.dataPath
prop = error.dataPath[1..]

View file

@ -356,7 +356,7 @@
submit_patch: "Submit Patch"
submit_changes: "Submit Changes"
save_changes: "Save Changes"
required_field: "Required field"
required_field: "required" # {change}
general:
and: "and"
@ -885,7 +885,9 @@
evaluate_recommend: "Evaluate/Recommend"
approve_funds: "Approve Funds"
no_purchaser_role: "No role in purchase decisions"
organization_label: "Name of School/District"
district_label: "District"
district_na: "Enter N/A if not applicable"
organization_label: "School" # {change}
city: "City"
state: "State"
country: "Country"

View file

@ -1,96 +0,0 @@
@import "app/styles/mixins"
@import "app/styles/bootstrap/variables"
#request-quote-view
#site-content-area
margin: 50px 0 100px
.row
margin: 20px 0
#conversion-warning
margin-top: 20px
.form-group
label
margin-bottom: 0
label.checkbox
font-weight: normal
.help-block
margin: -4px 0 2px
p
margin: 0 0 20px
.checkbox, .checkbox-inline
input
margin-top: 8px
#other-education-level-input
label
display: inline-block
display: inline-block
width: 200px
margin-left: 5px
#submit-request-btn
margin-left: 10px
// After submit (anonymous)
h5
margin-top: 50px
#social-network-signups
margin: 20px 0
button
margin-left: 10px
.text-h1
margin: 40px 0 30px
.algolia-autocomplete
width: 100%;
.aa-input
width: 100%
.aa-hint
color: #999
width: 100%
.aa-dropdown-menu
background-color: #fff
border: 1px solid #999
border-top: none
width: 100%
.aa-suggestion
cursor: pointer
padding: 5px 4px
border-top: 1px solid #ccc
.school
font-family: Open Sans
font-size: 14px
line-height: 20px
font-weight: bold
.district
font-family: Open Sans
font-size: 14px
line-height: 20px
span
white-space: nowrap
.aa-suggestion.aa-cursor
background-color: #B2D7FF
em
font-weight: bold
font-style: normal

View file

@ -0,0 +1,81 @@
#create-teacher-account-view, #convert-to-teacher-account-view, #request-quote-view
.algolia-autocomplete
width: 100%;
.aa-input
width: 100%
.aa-hint
color: #999
width: 100%
.aa-dropdown-menu
background-color: #fff
border: 1px solid #999
border-top: none
width: 100%
.aa-suggestion
cursor: pointer
padding: 5px 4px
border-top: 1px solid #ccc
.school
font-family: Open Sans
font-size: 14px
line-height: 20px
font-weight: bold
.district
font-family: Open Sans
font-size: 14px
line-height: 20px
span
white-space: nowrap
.aa-suggestion.aa-cursor
background-color: #B2D7FF
em
font-weight: bold
font-style: normal
// TODO: update form validation instead of overwriting these styles
.control-label
font-weight: bold
width: 100%
.error-help-block
margin-top: inherit
margin-bottom: 0px
float: right
font-size: 13px
font-style: italic
font-weight: normal
.text-muted
float: right
font-size: 13px
font-style: italic
font-weight: normal
.nullify-form-control
display: inherit
width: inherit
height: inherit
padding: inherit
font-size: inherit
line-height: inherit
color: inherit
vertical-align: inherit
background-color: inherit
background-image: inherit
border: inherit
border-radius: inherit
-webkit-box-shadow: inherit
box-shadow: inherit
-webkit-transition: inherit
transition: inherit

View file

@ -30,8 +30,8 @@ block content
thead
tr
th Created
th NCES District
th School Name
th School District
th.number NCES District Schools
th.number NCES District Students
th.number NCES School Students
@ -44,8 +44,8 @@ block content
- continue;
tr
td.created= trialRequest.get('created').substring(0, 10)
td= trialRequest.get('properties').nces_district || ''
td= trialRequest.get('properties').organization || ''
td= trialRequest.get('properties').nces_name || trialRequest.get('properties').organization || ''
td= trialRequest.get('properties').nces_district || trialRequest.get('properties').district || ''
td= trialRequest.get('properties').nces_district_schools || ''
td= trialRequest.get('properties').nces_district_students || ''
td= trialRequest.get('properties').nces_students || ''

View file

@ -28,40 +28,36 @@ block content
.row.m-y-2
.col-md-offset-2.col-md-4.col-sm-6
.form-group
label.control-label(data-i18n="general.username")
span.control-label(data-i18n="general.username")
input.form-control(disabled=true value=me.get('name'))
.col-md-4.col-sm-6
#email-form-group.form-group
label.control-label(data-i18n="general.email")
span.control-label(data-i18n="general.email")
input.form-control(name='email' disabled=true value=me.get('email'))
.row.m-y-2
.col-md-offset-2.col-md-4.col-sm-6
.form-group
label.control-label(data-i18n="general.first_name")
span.control-label(data-i18n="general.first_name")
input.form-control(name="firstName" value=me.get('firstName') || '')
.col-md-4.col-sm-6
.form-group
label.control-label(data-i18n="general.last_name")
span.control-label(data-i18n="general.last_name")
input.form-control(name="lastName" value=me.get('lastName') || '')
.row.m-y-2
.col-md-offset-2.col-md-4.col-sm-6
.form-group
label.control-label
span.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")
input.form-control(name="phoneNumber", data-i18n="[placeholder]teachers_quote.phone_number_help")
.col-md-4.col-sm-6
.form-group
label.control-label(data-i18n="teachers_quote.primary_role_label")
.help-block.small
em.text-info(data-i18n="teachers_quote.role_help")
span.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")
@ -75,30 +71,40 @@ block content
.row.m-y-2
.col-md-offset-2.col-md-4.col-sm-6
.form-group
label.control-label(data-i18n="teachers_quote.organization_label")
span.control-label
span(data-i18n="teachers_quote.organization_label")
span.spl.text-muted(data-i18n="signup.optional")
input.form-control#organization-control(name="organization")
.col-md-4.col-sm-6
.form-group
label.control-label(data-i18n="teachers_quote.city")
input.form-control(name="city")
//- TODO: algolia and form errors both change form-control
//- TODO: District not red on validation error
span.control-label.form-control.nullify-form-control(data-i18n="teachers_quote.district_label")
input.form-control#district-control(name="district", data-i18n="[placeholder]teachers_quote.district_na")
.row.m-y-2
.col-md-offset-2.col-md-4.col-sm-6
.form-group
label.control-label(data-i18n="teachers_quote.state")
input.form-control(name="state")
span.control-label(data-i18n="teachers_quote.city")
input.form-control(name="city")
.col-md-4.col-sm-6
.form-group
label.control-labellabel.control-label(data-i18n="teachers_quote.country")
span.control-label(data-i18n="teachers_quote.state")
input.form-control(name="state")
.row.m-y-2
.col-md-offset-2.col-md-4.col-sm-6
.form-group
span.control-labelspan.control-label(data-i18n="teachers_quote.country")
input.form-control(name="country")
#form-students-info
.row.m-y-2
.col-md-offset-2.col-md-4
.form-group
label.control-label(data-i18n="courses.number_programming_students")
span.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")
@ -113,7 +119,7 @@ block content
.col-md-4.col-sm-6
.form-group
label.control-label
span.control-label
span(data-i18n="courses.number_total_students")
span.spl.text-muted(data-i18n="signup.optional")
select.form-control(name="numStudentsTotal")
@ -126,11 +132,10 @@ block content
.form-group
.row.m-y-2
.col-md-offset-2.col-md-10
label.control-label(data-i18n="teachers_quote.education_level_label")
.col-md-offset-2.col-md-4
span.control-label(data-i18n="teachers_quote.education_level_label")
.help-block.small
em.text-info(data-i18n="teachers_quote.education_level_help")
.col-md-offset-2.col-md-5
.checkbox
label
input(type="checkbox" name="educationLevel" value="Elementary")
@ -156,7 +161,7 @@ block content
#anything-else-row.row.m-y-2
.col-md-offset-2.col-md-8
label.control-label
span.control-label
span(data-i18n="teachers_quote.anything_else")
span.spl.text-muted(data-i18n="signup.optional")
textarea.form-control(rows=8, name="notes")

View file

@ -34,49 +34,47 @@ block content
.row.m-y-2
.col-md-offset-2.col-md-4.col-sm-6
.form-group
label.control-label(data-i18n="general.username")
span.control-label(data-i18n="general.username")
input.form-control(name="name")
.col-md-4.col-sm-6
#email-form-group.form-group
label.control-label(data-i18n="general.email")
span.control-label(data-i18n="general.email")
input.form-control(name="email")
.row.m-y-2
.col-md-offset-2.col-md-4.col-sm-6
.form-group
label.control-label(data-i18n="general.first_name")
span.control-label(data-i18n="general.first_name")
input.form-control(name="firstName")
.col-md-4.col-sm-6
.form-group
label.control-label(data-i18n="general.last_name")
span.control-label(data-i18n="general.last_name")
input.form-control(name="lastName")
.row.m-y-2
.col-md-offset-2.col-md-4.col-sm-6
.form-group
label.control-label(data-i18n="general.password")
span.control-label(data-i18n="general.password")
input.form-control(name="password1", type="password")
.col-md-4.col-sm-6
.form-group
label.control-label(data-i18n="general.confirm_password")
span.control-label(data-i18n="general.confirm_password")
input.form-control(name="password2", type="password")
.row.m-y-2
.col-md-offset-2.col-md-4.col-sm-6
.form-group
label.control-label
span.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")
input.form-control(name="phoneNumber", data-i18n="[placeholder]teachers_quote.phone_number_help")
.col-md-4.col-sm-6
.form-group
label.control-label(data-i18n="teachers_quote.primary_role_label")
span.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")
@ -90,30 +88,40 @@ block content
.row.m-y-2
.col-md-offset-2.col-md-4.col-sm-6
.form-group
label.control-label(data-i18n="teachers_quote.organization_label")
span.control-label
span(data-i18n="teachers_quote.organization_label")
span.spl.text-muted(data-i18n="signup.optional")
input.form-control#organization-control(name="organization")
.col-md-4.col-sm-6
.form-group
label.control-label(data-i18n="teachers_quote.city")
input.form-control(name="city")
//- TODO: algolia and form errors both change form-control
//- TODO: District not red on validation error
span.control-label.form-control.nullify-form-control(data-i18n="teachers_quote.district_label")
input.form-control#district-control(name="district", data-i18n="[placeholder]teachers_quote.district_na")
.row.m-y-2
.col-md-offset-2.col-md-4.col-sm-6
.form-group
label.control-label(data-i18n="teachers_quote.state")
input.form-control(name="state")
span.control-label(data-i18n="teachers_quote.city")
input.form-control(name="city")
.col-md-4.col-sm-6
.form-group
label.control-labellabel.control-label(data-i18n="teachers_quote.country")
span.control-label(data-i18n="teachers_quote.state")
input.form-control(name="state")
.row.m-y-2
.col-md-offset-2.col-md-4.col-sm-6
.form-group
span.control-label(data-i18n="teachers_quote.country")
input.form-control(name="country")
#form-students-info
.row.m-y-2
.col-md-offset-2.col-md-4
.form-group
label.control-label(data-i18n="courses.number_programming_students")
span.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,7 +136,7 @@ block content
.col-md-4.col-sm-6
.form-group
label.control-label
span.control-label
span(data-i18n="courses.number_total_students")
span.spl.text-muted(data-i18n="signup.optional")
select.form-control(name="numStudentsTotal")
@ -140,14 +148,11 @@ block content
option 10,000+
.form-group
.row.m-y-2
.col-md-offset-2.col-md-10
label.control-label(data-i18n="teachers_quote.education_level_label")
.help-block.small
em.text-info(data-i18n="teachers_quote.education_level_help")
.col-md-offset-2.col-md-5
.col-md-offset-2.col-md-4
span.control-label(data-i18n="teachers_quote.education_level_label")
.help-block.small
em.text-info(data-i18n="teachers_quote.education_level_help")
.checkbox
label
input(type="checkbox" name="educationLevel" value="Elementary")
@ -173,7 +178,7 @@ block content
#anything-else-row.row.m-y-2
.col-md-offset-2.col-md-8
label.control-label
span.control-label
span(data-i18n="teachers_quote.anything_else")
span.spl.text-muted(data-i18n="signup.optional")

View file

@ -36,7 +36,7 @@ block content
.row
.col-md-offset-2.col-md-4.col-sm-6
.form-group
label.control-label
span.control-label
span(data-i18n="general.username")
span.spl.text-muted(data-i18n="signup.optional")
- var name = me.get('name') || '';
@ -44,20 +44,20 @@ block content
.col-md-4.col-sm-6
.form-group
label.control-label(data-i18n="share_progress_modal.form_label")
span.control-label(data-i18n="share_progress_modal.form_label")
- var email = me.get('email') || '';
input.form-control(name="email" value=email, disabled=!!email)
.row
.col-md-offset-2.col-md-4.col-sm-6
.form-group
label.control-label(data-i18n="general.first_name")
span.control-label(data-i18n="general.first_name")
- var firstName = me.get('firstName') || '';
input.form-control(name="firstName" value=firstName, disabled=!!firstName)
.col-md-4.col-sm-6
.form-group
label.control-label(data-i18n="general.last_name")
span.control-label(data-i18n="general.last_name")
- var lastName = me.get('lastName') || '';
input.form-control(name="lastName" value=lastName, disabled=!!lastName)
@ -65,33 +65,30 @@ block content
.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")
span.control-label(data-i18n="teachers_quote.phone_number")
input.form-control(name="phoneNumber", data-i18n="[placeholder]teachers_quote.phone_number_help")
if me.isAnonymous()
.row
.col-md-offset-2.col-md-4.col-sm-6
#email-form-group.form-group
label.control-label(data-i18n="general.email")
span.control-label(data-i18n="general.email")
- 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.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")
input.form-control(name="phoneNumber")
.row
.col-md-offset-2.col-md-4.col-sm-6
.form-group
label.control-label(data-i18n="teachers_quote.primary_role_label")
span.control-label(data-i18n="teachers_quote.primary_role_label")
select.form-control(name="role")
option(data-i18n="teachers_quote.primary_role_default", , value='')
option(data-i18n="courses.teacher", value="Teacher")
@ -100,45 +97,55 @@ block content
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
.col-md-4.col-sm-6
.form-group
label.control-label(data-i18n="teachers_quote.purchaser_role_label")
span.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
.row.m-y-2
.col-md-offset-2.col-md-4.col-sm-6
.form-group
label.control-label(data-i18n="teachers_quote.organization_label")
span.control-label
span(data-i18n="teachers_quote.organization_label")
span.spl.text-muted(data-i18n="signup.optional")
input.form-control#organization-control(name="organization")
.col-md-4.col-sm-6
.form-group
label.control-label(data-i18n="teachers_quote.city")
input.form-control(name="city")
//- TODO: algolia and form errors both change form-control
//- TODO: District not red on validation error
span.control-label.form-control.nullify-form-control(data-i18n="teachers_quote.district_label")
input.form-control#district-control(name="district", data-i18n="[placeholder]teachers_quote.district_na")
.row
.row.m-y-2
.col-md-offset-2.col-md-4.col-sm-6
.form-group
label.control-label(data-i18n="teachers_quote.state")
input.form-control(name="state")
span.control-label(data-i18n="teachers_quote.city")
input.form-control(name="city")
.col-md-4.col-sm-6
.form-group
label.control-labellabel.control-label(data-i18n="teachers_quote.country")
span.control-label(data-i18n="teachers_quote.state")
input.form-control(name="state")
.row.m-y-2
.col-md-offset-2.col-md-4.col-sm-6
.form-group
span.control-labelspan.control-label(data-i18n="teachers_quote.country")
input.form-control(name="country")
#form-students-info
.row
.col-md-offset-2.col-md-4
.form-group
label.control-label(data-i18n="courses.number_programming_students")
span.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")
@ -150,10 +157,10 @@ 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")
span.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
@ -163,14 +170,11 @@ block content
option 10,000+
.form-group
.row
.col-md-offset-2.col-md-10
label.control-label(data-i18n="teachers_quote.education_level_label")
.help-block.small
em.text-info(data-i18n="teachers_quote.education_level_help")
.col-md-offset-2.col-md-5
.row.m-y-2
.col-md-offset-2.col-md-4
span.control-label(data-i18n="teachers_quote.education_level_label")
.help-block.small
em.text-info(data-i18n="teachers_quote.education_level_help")
.checkbox
label
input(type="checkbox" name="educationLevel" value="Elementary")
@ -196,7 +200,7 @@ block content
#anything-else-row.row
.col-md-offset-2.col-md-8
label.control-label
span.control-label
span(data-i18n="teachers_quote.anything_else")
span.spl.text-muted(data-i18n="signup.optional")
@ -210,7 +214,7 @@ block content
input(type="hidden" name="nces_students")
input(type="hidden" name="nces_phone")
#buttons-row.row.text-center
#buttons-row.row.m-y-2.text-center
input#submit-request-btn.btn.btn-lg.btn-primary(type="submit" data-i18n="[value]teachers_quote.title")
#form-submit-success.text-center(class=showDone ? '' : 'hide')
@ -219,7 +223,7 @@ block content
p
span.spr(data-i18n="teachers_quote.thanks_p")
a.spl(href="mailto:team@codecombat.com") team@codecombat.com
unless me.isAnonymous()
a.btn.btn-lg.btn-navy(href="/teachers/classes")
span(data-i18n='teachers_quote.back_to_classes')
@ -244,17 +248,17 @@ block content
.row
.col-md-offset-2.col-md-4
.form-group
label.control-label(data-i18n="general.username")
span.control-label(data-i18n="general.username")
input.form-control(name="name")
.row
.col-md-offset-2.col-md-4
.form-group
label.control-label(data-i18n="general.password")
span.control-label(data-i18n="general.password")
input.form-control(name="password1", type="password")
.col-md-4
.form-group
label.control-label(data-i18n="general.confirm_password")
span.control-label(data-i18n="general.confirm_password")
input.form-control(name="password2", type="password")
.text-center

View file

@ -7,9 +7,9 @@ errors = require 'core/errors'
User = require 'models/User'
ConfirmModal = require 'views/editor/modal/ConfirmModal'
algolia = require 'core/services/algolia'
NCES_KEYS = ['id', 'name', 'district', 'district_id', 'district_schools', 'district_students', 'students', 'phone']
FORM_KEY = 'request-quote-form'
DISTRICT_NCES_KEYS = ['district', 'district_id', 'district_schools', 'district_students', 'phone']
SCHOOL_NCES_KEYS = DISTRICT_NCES_KEYS.concat(['id', 'name', 'students'])
module.exports = class ConvertToTeacherAccountView extends RootView
id: 'convert-to-teacher-account-view'
@ -40,13 +40,13 @@ module.exports = class ConvertToTeacherAccountView extends RootView
return 'Your account has not been updated! If you continue, your changes will be lost.'
invalidateNCES: ->
for key in NCES_KEYS
for key in SCHOOL_NCES_KEYS
@$('input[name="nces_' + key + '"]').val ''
onLoaded: ->
if @trialRequests.size() and me.isTeacher()
return application.router.navigate('/teachers', { trigger: true, replace: true })
super()
afterRender: ->
@ -75,16 +75,34 @@ module.exports = class ConvertToTeacherAccountView extends RootView
"<div class='school'> #{hr.name.value} </div>" +
"<div class='district'>#{hr.district.value}, " +
"<span>#{hr.city?.value}, #{hr.state.value}</span></div>"
]).on 'autocomplete:selected', (event, suggestion, dataset) =>
@$('input[name="district"]').val suggestion.district
@$('input[name="city"]').val suggestion.city
@$('input[name="state"]').val suggestion.state
@$('input[name="district"]').val suggestion.district
@$('input[name="country"]').val 'USA'
for key in NCES_KEYS
for key in SCHOOL_NCES_KEYS
@$('input[name="nces_' + key + '"]').val suggestion[key]
@onChangeForm()
$("#district-control").algolia_autocomplete({hint: false}, [
source: (query, callback) ->
algolia.schoolsIndex.search(query, { hitsPerPage: 5, aroundLatLngViaIP: false }).then (answer) ->
callback answer.hits
, ->
callback []
displayKey: 'district',
templates:
suggestion: (suggestion) ->
hr = suggestion._highlightResult
"<div class='district'>#{hr.district.value}, " +
"<span>#{hr.city?.value}, #{hr.state.value}</span></div>"
]).on 'autocomplete:selected', (event, suggestion, dataset) =>
@$('input[name="organization"]').val '' # TODO: does not persist on tabbing: back to school, back to district
@$('input[name="city"]').val suggestion.city
@$('input[name="state"]').val suggestion.state
@$('input[name="country"]').val 'USA'
for key in DISTRICT_NCES_KEYS
@$('input[name="nces_' + key + '"]').val suggestion[key]
@onChangeForm()
onChangeForm: ->
@ -97,28 +115,35 @@ module.exports = class ConvertToTeacherAccountView extends RootView
form = @$('form')
attrs = forms.formToObject(form)
trialRequestAttrs = _.cloneDeep(attrs)
# Don't save n/a district entries, but do validate required district client-side
trialRequestAttrs = _.omit(trialRequestAttrs, 'district') if trialRequestAttrs.district?.replace(/\s/ig, '').match(/n\/a/ig)
if @$('#other-education-level-checkbox').is(':checked')
val = @$('#other-education-level-input').val()
attrs.educationLevel.push(val) if val
trialRequestAttrs.educationLevel.push(val) if val
forms.clearFormAlerts(form)
result = tv4.validateMultiple(attrs, formSchema)
result = tv4.validateMultiple(trialRequestAttrs, formSchema)
error = false
if not result.valid
forms.applyErrorsToForm(form, result.errors)
error = true
if not _.size(attrs.educationLevel)
forms.setErrorToProperty(form, 'educationLevel', 'Include at least one.')
if not _.size(trialRequestAttrs.educationLevel)
forms.setErrorToProperty(form, 'educationLevel', 'include at least one')
error = true
unless attrs.district
forms.setErrorToProperty(form, 'district', $.i18n.t('common.required_field'))
error = true
if error
forms.scrollToFirstError()
return
attrs['siteOrigin'] = 'convert teacher'
trialRequestAttrs['siteOrigin'] = 'convert teacher'
@trialRequest = new TrialRequest({
type: 'course'
properties: attrs
properties: trialRequestAttrs
})
if me.get('role') is 'student' and not me.isAnonymous()
modal = new ConfirmModal({
@ -151,15 +176,14 @@ module.exports = class ConvertToTeacherAccountView extends RootView
formSchema = {
type: 'object'
required: [
'firstName', 'lastName', 'organization', 'role', 'numStudents', 'city', 'state', 'country'
]
required: ['firstName', 'lastName', 'role', 'numStudents', 'city', 'state', 'country']
properties:
firstName: { type: 'string' }
lastName: { type: 'string' }
phoneNumber: { type: 'string' }
role: { type: 'string' }
organization: { type: 'string' }
district: { type: 'string' }
city: { type: 'string' }
state: { type: 'string' }
country: { type: 'string' }
@ -172,5 +196,5 @@ formSchema = {
notes: { type: 'string' }
}
for key in NCES_KEYS
for key in SCHOOL_NCES_KEYS
formSchema['nces_' + key] = type: 'string'

View file

@ -7,9 +7,9 @@ errors = require 'core/errors'
User = require 'models/User'
algolia = require 'core/services/algolia'
FORM_KEY = 'request-quote-form'
SIGNUP_REDIRECT = '/teachers/classes'
NCES_KEYS = ['id', 'name', 'district', 'district_id', 'district_schools', 'district_students', 'students', 'phone']
DISTRICT_NCES_KEYS = ['district', 'district_id', 'district_schools', 'district_students', 'phone']
SCHOOL_NCES_KEYS = DISTRICT_NCES_KEYS.concat(['id', 'name', 'students'])
module.exports = class CreateTeacherAccountView extends RootView
id: 'create-teacher-account-view'
@ -43,12 +43,12 @@ module.exports = class CreateTeacherAccountView extends RootView
super()
invalidateNCES: ->
for key in NCES_KEYS
for key in SCHOOL_NCES_KEYS
@$('input[name="nces_' + key + '"]').val ''
afterRender: ->
super()
# apply existing trial request on form
properties = @trialRequest.get('properties')
if properties
@ -58,7 +58,7 @@ module.exports = class CreateTeacherAccountView extends RootView
otherLevel = _.first(_.difference(submittedLevels, commonLevels)) or ''
@$('#other-education-level-checkbox').attr('checked', !!otherLevel)
@$('#other-education-level-input').val(otherLevel)
$("#organization-control").algolia_autocomplete({hint: false}, [
source: (query, callback) ->
algolia.schoolsIndex.search(query, { hitsPerPage: 5, aroundLatLngViaIP: false }).then (answer) ->
@ -72,16 +72,34 @@ module.exports = class CreateTeacherAccountView extends RootView
"<div class='school'> #{hr.name.value} </div>" +
"<div class='district'>#{hr.district.value}, " +
"<span>#{hr.city?.value}, #{hr.state.value}</span></div>"
]).on 'autocomplete:selected', (event, suggestion, dataset) =>
@$('input[name="district"]').val suggestion.district
@$('input[name="city"]').val suggestion.city
@$('input[name="state"]').val suggestion.state
@$('input[name="district"]').val suggestion.district
@$('input[name="country"]').val 'USA'
for key in NCES_KEYS
for key in SCHOOL_NCES_KEYS
@$('input[name="nces_' + key + '"]').val suggestion[key]
@onChangeForm()
$("#district-control").algolia_autocomplete({hint: false}, [
source: (query, callback) ->
algolia.schoolsIndex.search(query, { hitsPerPage: 5, aroundLatLngViaIP: false }).then (answer) ->
callback answer.hits
, ->
callback []
displayKey: 'district',
templates:
suggestion: (suggestion) ->
hr = suggestion._highlightResult
"<div class='district'>#{hr.district.value}, " +
"<span>#{hr.city?.value}, #{hr.state.value}</span></div>"
]).on 'autocomplete:selected', (event, suggestion, dataset) =>
@$('input[name="organization"]').val '' # TODO: does not persist on tabbing: back to school, back to district
@$('input[name="city"]').val suggestion.city
@$('input[name="state"]').val suggestion.state
@$('input[name="country"]').val 'USA'
for key in DISTRICT_NCES_KEYS
@$('input[name="nces_' + key + '"]').val suggestion[key]
@onChangeForm()
onClickLoginLink: ->
@ -95,35 +113,41 @@ module.exports = class CreateTeacherAccountView extends RootView
onSubmitForm: (e) ->
e.preventDefault()
# Creating Trial Request first, validate user attributes but do not use them
form = @$('form')
allAttrs = forms.formToObject(form)
trialRequestAttrs = _.omit(allAttrs, 'name', 'password1', 'password2')
# Don't save n/a district entries, but do validate required district client-side
trialRequestAttrs = _.omit(trialRequestAttrs, 'district') if trialRequestAttrs.district?.replace(/\s/ig, '').match(/n\/a/ig)
if @$('#other-education-level-checkbox').is(':checked')
val = @$('#other-education-level-input').val()
trialRequestAttrs.educationLevel.push(val) if val
forms.clearFormAlerts(form)
result = tv4.validateMultiple(trialRequestAttrs, formSchema)
error = false
if not result.valid
forms.applyErrorsToForm(form, result.errors)
error = true
if not forms.validateEmail(trialRequestAttrs.email)
forms.setErrorToProperty(form, 'email', 'Invalid email.')
if not error and not forms.validateEmail(trialRequestAttrs.email)
forms.setErrorToProperty(form, 'email', 'invalid email')
error = true
if not _.size(trialRequestAttrs.educationLevel)
forms.setErrorToProperty(form, 'educationLevel', 'Include at least one.')
forms.setErrorToProperty(form, 'educationLevel', 'include at least one')
error = true
unless allAttrs.district
forms.setErrorToProperty(form, 'district', $.i18n.t('common.required_field'))
error = true
unless @gplusAttrs or @facebookAttrs
if not allAttrs.password1
forms.setErrorToProperty(form, 'password1', 'Required field')
forms.setErrorToProperty(form, 'password1', $.i18n.t('common.required_field'))
error = true
else if not allAttrs.password2
forms.setErrorToProperty(form, 'password2', 'Required field')
forms.setErrorToProperty(form, 'password2', $.i18n.t('common.required_field'))
error = true
else if allAttrs.password1 isnt allAttrs.password2
forms.setErrorToProperty(form, 'password1', 'Password fields are not equivalent')
@ -151,7 +175,7 @@ module.exports = class CreateTeacherAccountView extends RootView
.addClass('has-error')
.append($("<div class='help-block error-help-block'>#{userExists} <a class='login-link'>#{logIn}</a>"))
forms.scrollToFirstError()
else
else
errors.showNotyNetworkError(arguments...)
onClickEmailExistsLoginLink: ->
@ -225,7 +249,6 @@ module.exports = class CreateTeacherAccountView extends RootView
@$('input[type="password"]').attr('disabled', true)
@$('#gplus-logged-in-row, #social-network-signups').toggleClass('hide')
# Facebook signup
onClickFacebookSignupButton: ->
@ -269,13 +292,9 @@ module.exports = class CreateTeacherAccountView extends RootView
@$('#facebook-logged-in-row, #social-network-signups').toggleClass('hide')
formSchema = {
type: 'object'
required: [
'firstName', 'lastName', 'email', 'organization', 'role', 'numStudents', 'city'
'state', 'country'
]
required: ['firstName', 'lastName', 'email', 'role', 'numStudents', 'city', 'state', 'country']
properties:
password1: { type: 'string' }
password2: { type: 'string' }
@ -286,6 +305,7 @@ formSchema = {
phoneNumber: { type: 'string' }
role: { type: 'string' }
organization: { type: 'string' }
district: { type: 'string' }
city: { type: 'string' }
state: { type: 'string' }
country: { type: 'string' }
@ -298,5 +318,5 @@ formSchema = {
notes: { type: 'string' }
}
for key in NCES_KEYS
for key in SCHOOL_NCES_KEYS
formSchema['nces_' + key] = type: 'string'

View file

@ -8,7 +8,8 @@ ConfirmModal = require 'views/editor/modal/ConfirmModal'
algolia = require 'core/services/algolia'
SIGNUP_REDIRECT = '/teachers'
NCES_KEYS = ['id', 'name', 'district', 'district_id', 'district_schools', 'district_students', 'students', 'phone']
DISTRICT_NCES_KEYS = ['district', 'district_id', 'district_schools', 'district_students', 'phone']
SCHOOL_NCES_KEYS = DISTRICT_NCES_KEYS.concat(['id', 'name', 'students'])
module.exports = class RequestQuoteView extends RootView
id: 'request-quote-view'
@ -46,7 +47,7 @@ module.exports = class RequestQuoteView extends RootView
super()
invalidateNCES: ->
for key in NCES_KEYS
for key in SCHOOL_NCES_KEYS
@$('input[name="nces_' + key + '"]').val ''
afterRender: ->
@ -75,16 +76,34 @@ module.exports = class RequestQuoteView extends RootView
"<div class='school'> #{hr.name.value} </div>" +
"<div class='district'>#{hr.district.value}, " +
"<span>#{hr.city?.value}, #{hr.state.value}</span></div>"
]).on 'autocomplete:selected', (event, suggestion, dataset) =>
@$('input[name="district"]').val suggestion.district
@$('input[name="city"]').val suggestion.city
@$('input[name="state"]').val suggestion.state
@$('input[name="district"]').val suggestion.district
@$('input[name="country"]').val 'USA'
for key in NCES_KEYS
for key in SCHOOL_NCES_KEYS
@$('input[name="nces_' + key + '"]').val suggestion[key]
@onChangeRequestForm()
$("#district-control").algolia_autocomplete({hint: false}, [
source: (query, callback) ->
algolia.schoolsIndex.search(query, { hitsPerPage: 5, aroundLatLngViaIP: false }).then (answer) ->
callback answer.hits
, ->
callback []
displayKey: 'district',
templates:
suggestion: (suggestion) ->
hr = suggestion._highlightResult
"<div class='district'>#{hr.district.value}, " +
"<span>#{hr.city?.value}, #{hr.state.value}</span></div>"
]).on 'autocomplete:selected', (event, suggestion, dataset) =>
@$('input[name="organization"]').val '' # TODO: does not persist on tabbing: back to school, back to district
@$('input[name="city"]').val suggestion.city
@$('input[name="state"]').val suggestion.state
@$('input[name="country"]').val 'USA'
for key in DISTRICT_NCES_KEYS
@$('input[name="nces_' + key + '"]').val suggestion[key]
@onChangeRequestForm()
onChangeRequestForm: ->
@ -96,32 +115,39 @@ module.exports = class RequestQuoteView extends RootView
e.preventDefault()
form = @$('#request-form')
attrs = forms.formToObject(form)
trialRequestAttrs = _.cloneDeep(attrs)
# Don't save n/a district entries, but do validate required district client-side
trialRequestAttrs = _.omit(trialRequestAttrs, 'district') if trialRequestAttrs.district?.replace(/\s/ig, '').match(/n\/a/ig)
# custom other input logic
if @$('#other-education-level-checkbox').is(':checked')
val = @$('#other-education-level-input').val()
attrs.educationLevel.push(val) if val
trialRequestAttrs.educationLevel.push(val) if val
forms.clearFormAlerts(form)
requestFormSchema = if me.isAnonymous() then requestFormSchemaAnonymous else requestFormSchemaLoggedIn
result = tv4.validateMultiple(attrs, requestFormSchemaAnonymous)
result = tv4.validateMultiple(trialRequestAttrs, requestFormSchemaAnonymous)
error = false
if not result.valid
forms.applyErrorsToForm(form, result.errors)
error = true
if not forms.validateEmail(attrs.email)
forms.setErrorToProperty(form, 'email', 'Invalid email.')
if not error and not forms.validateEmail(trialRequestAttrs.email)
forms.setErrorToProperty(form, 'email', 'invalid email')
error = true
if not _.size(attrs.educationLevel)
forms.setErrorToProperty(form, 'educationLevel', 'Include at least one.')
if not _.size(trialRequestAttrs.educationLevel)
forms.setErrorToProperty(form, 'educationLevel', 'include at least one')
error = true
unless attrs.district
forms.setErrorToProperty(form, 'district', $.i18n.t('common.required_field'))
error = true
if error
forms.scrollToFirstError()
return
attrs['siteOrigin'] = 'demo request'
trialRequestAttrs['siteOrigin'] = 'demo request'
@trialRequest = new TrialRequest({
type: 'course'
properties: attrs
properties: trialRequestAttrs
})
if me.get('role') is 'student' and not me.isAnonymous()
modal = new ConfirmModal({
@ -262,7 +288,7 @@ module.exports = class RequestQuoteView extends RootView
requestFormSchemaAnonymous = {
type: 'object'
required: [
'firstName', 'lastName', 'email', 'organization', 'role', 'purchaserRole', 'numStudents',
'firstName', 'lastName', 'email', 'role', 'purchaserRole', 'numStudents',
'numStudentsTotal', 'phoneNumber', 'city', 'state', 'country']
properties:
firstName: { type: 'string' }
@ -273,6 +299,7 @@ requestFormSchemaAnonymous = {
role: { type: 'string' }
purchaserRole: { type: 'string' }
organization: { type: 'string' }
district: { type: 'string' }
city: { type: 'string' }
state: { type: 'string' }
country: { type: 'string' }
@ -285,7 +312,7 @@ requestFormSchemaAnonymous = {
notes: { type: 'string' },
}
for key in NCES_KEYS
for key in SCHOOL_NCES_KEYS
requestFormSchemaAnonymous['nces_' + key] = type: 'string'
# same form, but add username input

View file

@ -34,7 +34,8 @@ module.exports = class TeachersContactModal extends ModalView
message = """
Hi CodeCombat! I want to learn more about the Classroom experience and get licenses so that my students can access Computer Science 2 and on.
Name of School/District: #{props.organization or ''}
Name of School #{props.nces_name or props.organization or ''}
Name of District: #{props.nces_district or props.district or ''}
Role: #{props.role or ''}
Phone Number: #{props.phoneNumber or ''}
"""

View file

@ -16,7 +16,7 @@ if (process.argv.length !== 10) {
// TODO: Cleanup country/status lookup code
// Save as custom fields instead of user-specific lead notes (also saving nces_ props)
const commonTrialProperties = ['organization', 'city', 'state', 'country'];
const commonTrialProperties = ['organization', 'district', 'city', 'state', 'country'];
// Old properties which are deprecated or moved
const customFieldsToRemove = [
@ -327,7 +327,7 @@ function findCocoLeads(done) {
if (!trialRequest.properties || !trialRequest.properties.email) continue;
const email = trialRequest.properties.email.toLowerCase();
emails.push(email);
const name = trialRequest.properties.nces_name || trialRequest.properties.organization || trialRequest.properties.school || email;
const name = trialRequest.properties.nces_name || trialRequest.properties.organization || trialRequest.properties.school || trialRequest.properties.district || trialRequest.properties.nces_district || email;
if (!leads[name]) leads[name] = new CocoLead(name);
leads[name].addTrialRequest(email, trialRequest);
emailLeadMap[email] = leads[name];

View file

@ -138,7 +138,7 @@ module.exports =
trialRequests = yield TrialRequest.find({$and: [{type: 'course'}, {applicant: {$in: userIDs}}]}, {applicant: 1, properties: 1}).lean()
schoolPrepaidsMap = {}
for trialRequest in trialRequests
school = trialRequest.properties?.organization ? trialRequest.properties?.school
school = trialRequest.properties?.nces_name ? trialRequest.properties?.organization ? trialRequest.properties?.school
continue unless school
if userPrepaidsMap[trialRequest.applicant.valueOf()]?.length > 0
schoolPrepaidsMap[school] ?= []

View file

@ -184,6 +184,7 @@ module.exports = {
email: 'an@email.com'
phoneNumber: '555-555-5555'
organization: 'Greendale'
district: 'Green District'
}
}, attrs)
}

View file

@ -31,6 +31,7 @@ describe 'ConvertToTeacherAccountView (/teachers/update-account)', ->
phoneNumber: '555-555-5555'
role: 'Teacher'
organization: 'School'
district: 'District'
city: 'Springfield'
state: 'AA'
country: 'asdf'
@ -168,3 +169,48 @@ describe 'ConvertToTeacherAccountView (/teachers/update-account)', ->
})
expect(me.get('role')).toBe(successForm.role.toLowerCase())
describe 'submitting the form without school', ->
beforeEach ->
view.$el.find('#request-form').trigger('change') # to confirm navigating away isn't prevented
form = view.$('form')
formData = _.omit(successForm, ['organization'])
forms.objectToForm(form, formData)
form.submit()
it 'submits a trial request, which does not include school setting', ->
request = jasmine.Ajax.requests.mostRecent()
expect(request.url).toBe('/db/trial.request')
expect(request.method).toBe('POST')
attrs = JSON.parse(request.params)
expect(attrs.properties?.organization).toBeUndefined()
expect(attrs.properties?.district).toEqual('District')
describe 'submitting the form without district', ->
beforeEach ->
view.$el.find('#request-form').trigger('change') # to confirm navigating away isn't prevented
form = view.$('form')
formData = _.omit(successForm, ['district'])
forms.objectToForm(form, formData)
form.submit()
it 'displays a validation error on district and not school', ->
expect(view.$('#organization-control').parent().hasClass('has-error')).toEqual(false)
expect(view.$('#district-control').parent().hasClass('has-error')).toEqual(true)
describe 'submitting the form district set to n/a', ->
beforeEach ->
view.$el.find('#request-form').trigger('change') # to confirm navigating away isn't prevented
form = view.$('form')
formData = _.omit(successForm, ['organization'])
formData.district = 'N/A'
forms.objectToForm(form, formData)
form.submit()
it 'submits a trial request, which does not include district setting', ->
expect(view.$('#organization-control').parent().hasClass('has-error')).toEqual(false)
expect(view.$('#district-control').parent().hasClass('has-error')).toEqual(false)
request = jasmine.Ajax.requests.mostRecent()
expect(request.url).toBe('/db/trial.request')
expect(request.method).toBe('POST')
attrs = JSON.parse(request.params)
expect(attrs.properties?.district).toBeUndefined()

View file

@ -33,6 +33,7 @@ describe 'CreateTeacherAccountView', ->
phoneNumber: '555-555-5555'
role: 'Teacher'
organization: 'School'
district: 'District'
city: 'Springfield'
state: 'AA'
country: 'asdf'
@ -219,6 +220,8 @@ describe 'CreateTeacherAccountView', ->
expect(attrs.password2).toBeUndefined()
expect(attrs.name).toBeUndefined()
expect(attrs.properties?.siteOrigin).toBe('create teacher')
expect(attrs.properties?.organization).toEqual('School')
expect(attrs.properties?.district).toEqual('District')
describe 'after saving the new trial request', ->
beforeEach ->
@ -266,5 +269,49 @@ describe 'CreateTeacherAccountView', ->
spyOn(view, 'openModalView')
view.$('#email-form-group .login-link').click()
expect(view.openModalView).toHaveBeenCalled()
describe 'submitting the form without school', ->
beforeEach ->
view.$el.find('#request-form').trigger('change') # to confirm navigating away isn't prevented
form = view.$('form')
formData = _.omit(successForm, ['organization'])
forms.objectToForm(form, formData)
form.submit()
it 'submits a trial request, which does not include school setting', ->
request = jasmine.Ajax.requests.mostRecent()
expect(request.url).toBe('/db/trial.request')
expect(request.method).toBe('POST')
attrs = JSON.parse(request.params)
expect(attrs.properties?.organization).toBeUndefined()
expect(attrs.properties?.district).toEqual('District')
describe 'submitting the form without district', ->
beforeEach ->
view.$el.find('#request-form').trigger('change') # to confirm navigating away isn't prevented
form = view.$('form')
formData = _.omit(successForm, ['district'])
forms.objectToForm(form, formData)
form.submit()
it 'displays a validation error on district and not school', ->
expect(view.$('#organization-control').parent().hasClass('has-error')).toEqual(false)
expect(view.$('#district-control').parent().hasClass('has-error')).toEqual(true)
describe 'submitting the form district set to n/a', ->
beforeEach ->
view.$el.find('#request-form').trigger('change') # to confirm navigating away isn't prevented
form = view.$('form')
formData = _.omit(successForm, ['organization'])
formData.district = 'N/A'
forms.objectToForm(form, formData)
form.submit()
it 'submits a trial request, which does not include district setting', ->
expect(view.$('#organization-control').parent().hasClass('has-error')).toEqual(false)
expect(view.$('#district-control').parent().hasClass('has-error')).toEqual(false)
request = jasmine.Ajax.requests.mostRecent()
expect(request.url).toBe('/db/trial.request')
expect(request.method).toBe('POST')
attrs = JSON.parse(request.params)
expect(attrs.properties?.district).toBeUndefined()

View file

@ -2,16 +2,17 @@ RequestQuoteView = require 'views/teachers/RequestQuoteView'
forms = require 'core/forms'
describe 'RequestQuoteView', ->
view = null
successFormValues = {
successForm = {
firstName: 'A'
lastName: 'B'
email: 'C@D.com'
phoneNumber: '555-555-5555'
role: 'Teacher'
organization: 'School'
district: 'District'
city: 'Springfield'
state: 'AA'
country: 'asdf'
@ -20,10 +21,10 @@ describe 'RequestQuoteView', ->
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', ->
describe 'when an anonymous user', ->
beforeEach (done) ->
me.clear()
me.set('_id', '1234')
@ -32,184 +33,220 @@ describe 'RequestQuoteView', ->
view = new RequestQuoteView()
view.render()
jasmine.demoEl(view.$el)
request = jasmine.Ajax.requests.mostRecent()
request.respondWith({
status: 200
responseText: JSON.stringify([{
_id: '1'
properties: {
firstName: 'First'
lastName: 'Last'
}
}])
})
_.defer done # Let SuperModel finish
it 'shows request received', ->
expect(view.$('#request-form').hasClass('hide')).toBe(true)
expect(view.$('#form-submit-success').hasClass('hide')).toBe(false)
describe 'when user is signed in and has an existing trial request', ->
beforeEach (done) ->
me.clear()
me.set('_id', '1234')
me._revertAttributes = {}
spyOn(me, 'isAnonymous').and.returnValue(false)
view = new RequestQuoteView()
view.render()
jasmine.demoEl(view.$el)
request = jasmine.Ajax.requests.mostRecent()
request.respondWith({
status: 200
responseText: JSON.stringify([{
_id: '1'
properties: {
firstName: 'First'
lastName: 'Last'
}
}])
})
_.defer done # Let SuperModel finish
it 'shows form with data from the most recent request', ->
expect(view.$('input[name="firstName"]').val()).toBe('First')
describe 'when a user is anonymous and does NOT have an existing trial request', ->
beforeEach (done) ->
me.clear()
me.set('_id', '1234')
me._revertAttributes = {}
spyOn(me, 'isAnonymous').and.returnValue(true)
view = new RequestQuoteView()
view.render()
jasmine.demoEl(view.$el)
request = jasmine.Ajax.requests.mostRecent()
request.respondWith({
status: 200
responseText: '[]'
})
_.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))
@submitRequest.respondWith({
status: 201
responseText: JSON.stringify(_.extend({_id: 'a'}, successFormValues))
describe 'has an existing trial request', ->
beforeEach (done) ->
request = jasmine.Ajax.requests.mostRecent()
request.respondWith({
status: 200
responseText: JSON.stringify([{
_id: '1'
properties: {
firstName: 'First'
lastName: 'Last'
}
}])
})
_.defer done # Let SuperModel finish
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')
attrs = JSON.parse(@submitRequest.params)
expect(attrs.properties?.siteOrigin).toBe('demo request')
it 'sets the user\'s role to the one they chose', ->
request = _.last(jasmine.Ajax.requests.filter((r) -> _.string.startsWith(r.url, '/db/user')))
expect(request).toBeTruthy()
expect(request.method).toBe('PUT')
expect(JSON.parse(request.params).role).toBe('teacher')
it 'shows a signup form', ->
expect(view.$('#form-submit-success').hasClass('hide')).toBe(false)
it 'shows request received', ->
expect(view.$('#request-form').hasClass('hide')).toBe(true)
describe 'signup form', ->
expect(view.$('#form-submit-success').hasClass('hide')).toBe(false)
describe 'does NOT have an existing trial request', ->
beforeEach (done) ->
request = jasmine.Ajax.requests.mostRecent()
request.respondWith({
status: 200
responseText: '[]'
})
_.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 ->
application.facebookHandler.fakeAPI()
application.gplusHandler.fakeAPI()
it 'fills the username field with the given first and last names', ->
expect(view.$('input[name="name"]').val()).toBe('A B')
it 'includes a facebook button which will sign them in immediately', ->
view.$('#facebook-signup-btn').click()
request = jasmine.Ajax.requests.mostRecent()
expect(request.method).toBe('PUT')
expect(request.url).toBe('/db/user?facebookID=abcd&facebookAccessToken=1234')
it 'includes a gplus button which will sign them in immediately', ->
view.$('#gplus-signup-btn').click()
request = jasmine.Ajax.requests.mostRecent()
expect(request.method).toBe('PUT')
expect(request.url).toBe('/db/user?gplusID=abcd&gplusAccessToken=1234')
it 'can sign them up with username and password', ->
form = view.$('#signup-form')
forms.objectToForm(form, {
password1: 'asdf'
password2: 'asdf'
name: 'some name'
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, successForm)
view.$('#request-form').submit()
@submitRequest = _.last(jasmine.Ajax.requests.filter(isSubmitRequest))
@submitRequest.respondWith({
status: 201
responseText: JSON.stringify(_.extend({_id: 'a'}, successForm))
})
form.submit()
request = jasmine.Ajax.requests.mostRecent()
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')
attrs = JSON.parse(@submitRequest.params)
expect(attrs.properties?.siteOrigin).toBe('demo request')
it 'sets the user\'s role to the one they chose', ->
request = _.last(jasmine.Ajax.requests.filter((r) -> _.string.startsWith(r.url, '/db/user')))
expect(request).toBeTruthy()
expect(request.method).toBe('PUT')
expect(request.url).toBe('/db/user/1234')
describe 'when an anonymous user tries to submit a request with an existing user\'s email', ->
expect(JSON.parse(request.params).role).toBe('teacher')
it 'shows a signup form', ->
expect(view.$('#form-submit-success').hasClass('hide')).toBe(false)
expect(view.$('#request-form').hasClass('hide')).toBe(true)
describe 'signup form', ->
beforeEach ->
application.facebookHandler.fakeAPI()
application.gplusHandler.fakeAPI()
it 'fills the username field with the given first and last names', ->
expect(view.$('input[name="name"]').val()).toBe('A B')
it 'includes a facebook button which will sign them in immediately', ->
view.$('#facebook-signup-btn').click()
request = jasmine.Ajax.requests.mostRecent()
expect(request.method).toBe('PUT')
expect(request.url).toBe('/db/user?facebookID=abcd&facebookAccessToken=1234')
it 'includes a gplus button which will sign them in immediately', ->
view.$('#gplus-signup-btn').click()
request = jasmine.Ajax.requests.mostRecent()
expect(request.method).toBe('PUT')
expect(request.url).toBe('/db/user?gplusID=abcd&gplusAccessToken=1234')
it 'can sign them up with username and password', ->
form = view.$('#signup-form')
forms.objectToForm(form, {
password1: 'asdf'
password2: 'asdf'
name: 'some name'
})
form.submit()
request = jasmine.Ajax.requests.mostRecent()
expect(request.method).toBe('PUT')
expect(request.url).toBe('/db/user/1234')
describe 'tries to submit a request with an existing user\'s email', ->
beforeEach ->
forms.objectToForm(view.$el, successFormValues)
forms.objectToForm(view.$el, successForm)
view.$('#request-form').submit()
@submitRequest = _.last(jasmine.Ajax.requests.filter(isSubmitRequest))
@submitRequest.respondWith({
status: 409
responseText: '{}'
})
it 'shows an error that the email already exists', ->
expect(view.$('#email-form-group').hasClass('has-error')).toBe(true)
expect(view.$('#email-form-group .error-help-block').length).toBe(1)
describe 'when user is signed in and has role "student"', ->
describe 'submits the form without school', ->
beforeEach ->
view.$el.find('#request-form').trigger('change') # to confirm navigating away isn't prevented
form = view.$('#request-form')
formData = _.omit(successForm, ['organization'])
forms.objectToForm(form, formData)
form.submit()
it 'submits a trial request, which does not include school setting', ->
request = jasmine.Ajax.requests.mostRecent()
expect(request.url).toBe('/db/trial.request')
expect(request.method).toBe('POST')
attrs = JSON.parse(request.params)
expect(attrs.properties?.organization).toBeUndefined()
expect(attrs.properties?.district).toEqual('District')
describe 'submits the form without district', ->
beforeEach ->
view.$el.find('#request-form').trigger('change') # to confirm navigating away isn't prevented
form = view.$('#request-form')
formData = _.omit(successForm, ['district'])
forms.objectToForm(form, formData)
form.submit()
it 'displays a validation error on district and not school', ->
expect(view.$('#organization-control').parent().hasClass('has-error')).toEqual(false)
expect(view.$('#district-control').parent().hasClass('has-error')).toEqual(true)
describe 'submits form with district set to n/a', ->
beforeEach ->
view.$el.find('#request-form').trigger('change') # to confirm navigating away isn't prevented
form = view.$('#request-form')
formData = _.omit(successForm, ['organization'])
formData.district = 'N/A'
forms.objectToForm(form, formData)
form.submit()
it 'submits a trial request, which does not include district setting', ->
expect(view.$('#organization-control').parent().hasClass('has-error')).toEqual(false)
expect(view.$('#district-control').parent().hasClass('has-error')).toEqual(false)
request = jasmine.Ajax.requests.mostRecent()
expect(request.url).toBe('/db/trial.request')
expect(request.method).toBe('POST')
attrs = JSON.parse(request.params)
expect(attrs.properties?.district).toBeUndefined()
describe 'when a signed in user', ->
beforeEach (done) ->
me.set('role', 'student')
me.set('name', 'Some User')
me.clear()
me.set('_id', '1234')
me._revertAttributes = {}
spyOn(me, 'isAnonymous').and.returnValue(false)
view = new RequestQuoteView()
view.render()
jasmine.demoEl(view.$el)
request = jasmine.Ajax.requests.mostRecent()
request.respondWith({ status: 200, responseText: '[]'})
_.defer done # Let SuperModel finish
it 'shows a conversion warning', ->
expect(view.$('#conversion-warning').length).toBe(1)
it 'requires confirmation to submit the form', ->
form = view.$('#request-form')
forms.objectToForm(form, successFormValues)
spyOn(view, 'openModalView')
form.submit()
expect(view.openModalView).toHaveBeenCalled()
submitRequest = _.last(jasmine.Ajax.requests.filter(isSubmitRequest))
expect(submitRequest).toBeFalsy()
confirmModal = view.openModalView.calls.argsFor(0)[0]
confirmModal.trigger 'confirm'
submitRequest = _.last(jasmine.Ajax.requests.filter(isSubmitRequest))
expect(submitRequest).toBeTruthy()
describe 'has an existing trial request', ->
beforeEach (done) ->
request = jasmine.Ajax.requests.mostRecent()
request.respondWith({
status: 200
responseText: JSON.stringify([{
_id: '1'
properties: {
firstName: 'First'
lastName: 'Last'
}
}])
})
_.defer done # Let SuperModel finish
it 'shows form with data from the most recent request', ->
expect(view.$('input[name="firstName"]').val()).toBe('First')
describe 'has role "student"', ->
beforeEach (done) ->
me.clear()
me.set('role', 'student')
me.set('name', 'Some User')
request = jasmine.Ajax.requests.mostRecent()
request.respondWith({ status: 200, responseText: '[]'})
_.defer done # Let SuperModel finish
it 'shows a conversion warning', ->
expect(view.$('#conversion-warning').length).toBe(1)
it 'requires confirmation to submit the form', ->
form = view.$('#request-form')
forms.objectToForm(form, successForm)
spyOn(view, 'openModalView')
form.submit()
expect(view.openModalView).toHaveBeenCalled()
submitRequest = _.last(jasmine.Ajax.requests.filter(isSubmitRequest))
expect(submitRequest).toBeFalsy()
confirmModal = view.openModalView.calls.argsFor(0)[0]
confirmModal.trigger 'confirm'
submitRequest = _.last(jasmine.Ajax.requests.filter(isSubmitRequest))
expect(submitRequest).toBeTruthy()