context = window
rest = context.JK.Rest()
logger = context.JK.logger
UserStore = context.UserStore
@BookLesson = React.createClass({
mixins: [
ICheckMixin,
Reflux.listenTo(AppStore, "onAppInit"),
Reflux.listenTo(UserStore, "onUserChanged")
]
onAppInit: (@app) ->
@app.bindScreen('jamclass/book-lesson',
{beforeShow: @beforeShow, afterShow: @afterShow, beforeHide: @beforeHide})
onUserChanged: (userState) ->
@setState({user: userState?.user})
checkboxChanged: (e) ->
checked = $(e.target).is(':checked')
value = $(e.target).val()
state = {}
state['lesson-frequency'] = value
@setState(state)
componentDidMount: () ->
@checkboxes = [{selector: 'input.lesson-freq-field', stateKey: 'lesson-frequency'}]
@root = $(@getDOMNode())
@slot1Date = @root.find('.slot-1 .date-picker')
@slot2Date = @root.find('.slot-2 .date-picker')
@slot1Date.datepicker({
dateFormat: "D M d yy",
onSelect: ((e) => @toggleDate(e))
})
@slot2Date.datepicker({
dateFormat: "D M d yy",
onSelect: ((e) => @toggleDate(e))
})
@iCheckify()
componentDidUpdate:() ->
@iCheckify()
@slot1Date = @root.find('.slot-1 .date-picker')
@slot2Date = @root.find('.slot-2 .date-picker')
@slot1Date.datepicker({
dateFormat: "D M d yy",
onSelect: ((e) => @toggleDate(e))
})
@slot2Date.datepicker({
dateFormat: "D M d yy",
onSelect: ((e) => @toggleDate(e))
})
toggleDate: (e) ->
isNormal: () ->
@state.type == 'normal'
isTestDrive: () ->
@state.type?.indexOf('test-drive') == 0
parseId:(id) ->
if !id?
{id: null, type: null}
else
bits = id.split('_')
if bits.length == 2
{id: bits[1], type: bits[0]}
else
{id: null, type: null}
beforeHide: (e) ->
logger.debug("BookLesson: beforeHide")
@resetErrors()
beforeShow: (e) ->
afterShow: (e) ->
logger.debug("BookLesson: afterShow", e.id)
parsed = @parseId(e.id)
id = parsed.id
@setState({teacherId: id, type: parsed.type})
@resetErrors()
rest.getUserDetail({
id: id,
show_teacher: true
}).done((response) => @userDetailDone(response)).fail(@app.ajaxError)
userDetailDone: (response) ->
if response.id == @state.teacherId
#school_on_school = response.teacher.school_id? && @state.user?.school_id? && response.teacher.school_id == @state.user.school_id && !response.teacher.school.education
@setState({teacher: response, isSelf: response.id == context.JK.currentUserId})
else
logger.debug("BookLesson: ignoring teacher details", response.id, @state.teacherId)
getInitialState: () ->
state = {
user: null,
teacher: null,
teacherId: null,
generalErrors: null,
descriptionErrors: null,
bookedPriceErrors: null,
slot1Errors: null,
slot2Errors: null
updating: false,
recurring: 'single'
}
state['lesson-frequency'] = 'single'
state
jamclassPolicies: (e) ->
e.preventDefault()
context.JK.popExternalLink($(e.target).attr('href'))
getSlotData: (position) ->
$slot = @root.find('.slot-' + (position + 1))
picker = $slot.find('.date-picker')
hour = $slot.find('.hour').val()
minute = $slot.find('.minute').val()
am_pm = $slot.find('.am_pm').val()
if hour? and hour != ''
hour = new Number(hour)
if am_pm == 'PM'
hour += 12
else
hour = null
if minute? and minute != ''
minute = new Number(minute)
else
minute = null
if !@isRecurring()
date = picker.datepicker("getDate")
if date?
date = context.JK.formatDateYYYYMMDD(date)
else
day_of_week = $slot.find('.day_of_week').val()
{hour: hour, minute: minute, date: date, day_of_week: day_of_week}
resetErrors: () ->
@setState({generalErrors: null, slot1Errors: null, slot2Errors: null, descriptionErrors: null, bookedPriceErrors: null})
isRecurring: () ->
@state['lesson-frequency'] == 'recurring'
#@state.recurring == 'recurring'
isMonthly: () ->
if !@isRecurring()
return false
parsed = @bookingOption()
return parsed? && parsed.frequency == 'monthly'
bookingOption: () ->
select = @root.find('.booking-options-for-teacher')
value = select.val()
@parseBookingOption(value)
# select format = frequency|lesson_length , where frequency is 'monthly' or 'weekly'
parseBookingOption: (value) ->
if !value?
return null
bits = value.split('|')
if !bits? || bits.length != 2
return null
return {frequency: bits[0], lesson_length: bits[1]}
onBookLesson: (e) ->
e.preventDefault()
logger.debug("user requested to book lesson")
if $(e.target).is('.disabled')
return
options = {}
options.teacher = this.state.teacher.id
options.slots = [@getSlotData(0), @getSlotData(1)]
options.timezone = window.jstz.determine().name()
description = @root.find('textarea.user-description').val()
if description == ''
description == null
options.description = description
if @isTestDrive()
options.payment_style = 'elsewhere'
options.lesson_type = 'test-drive'
else if @isNormal()
options.lesson_type = 'paid'
if @isRecurring()
if @isMonthly()
options.payment_style = 'monthly'
else
options.payment_style = 'weekly'
else
options.payment_style = 'single'
options.recurring = @isRecurring()
parsed = @bookingOption()
if parsed?
options.lesson_length = parsed.lesson_length
else
throw "Unable to determine lesson type"
logger.debug("lesson booking data: " + JSON.stringify(options))
@resetErrors()
@setState({updating: true})
rest.bookLesson(options).done((response) => @booked(response)).fail((jqXHR) => @failedBooking(jqXHR))
booked: (response) ->
@setState({updating: false})
UserActions.refresh()
#if response.user['has_stored_credit_card?'] || @state.school_on_school || response.posa_card_id?
if response.user['has_stored_credit_card?'] || (response.posa_card_id? && response.posa_card_purchased)
context.JK.Banner.showNotice("Lesson Requested","The teacher has been notified of your lesson request, and should respond soon.
We've taken you back to the JamClass home page, where you can check the status of this lesson, as well as any other past and future lessons.")
url = "/client#/jamclass/lesson-booking/#{response.id}"
url = "/client#/jamclass"
context.location = url
else
context.location = "/client#/jamclass/lesson-payment/lesson-booking_#{response.id}"
failedBooking: (jqXHR) ->
@setState({updating: false})
if jqXHR.status == 422
logger.debug("unable to book lesson: " + jqXHR.responseText)
body = JSON.parse(jqXHR.responseText)
generalErrors = {errors: {}}
for errorType, errors of body.errors
if errorType == 'description'
@setState({descriptionErrors: errors})
else if errorType == 'booked_price'
@setState({bookedPriceErrors: errors})
else if errorType == 'lesson_length'
# swallow, because 'booked_price' covers this
else if errorType == 'lesson_booking_slots'
# do nothing. these are handled better by the _children errors
else
generalErrors.errors[errorType] = errors
for childErrorType, childErrors of body._children
if childErrorType == 'lesson_booking_slots'
slot1Errors = childErrors[0]
slot2Errors = childErrors[1]
if Object.keys(slot1Errors["errors"]).length > 0
@setState({slot1Errors: slot1Errors})
if Object.keys(slot2Errors["errors"]).length > 0
@setState({slot2Errors: slot2Errors})
if Object.keys(generalErrors.errors).length > 0
@setState({generalErrors: generalErrors})
onCancel: (e) ->
e.preventDefault()
window.history.go(-1);
isNormal: () ->
@state.type == 'normal'
constructBookingOptions: () ->
results = []
if !@state.teacher?
return results
teacher = @state.teacher.teacher
enabledMinutes = []
for minutes in [30, 45, 60, 90, 120]
duration_enabled = teacher["lesson_duration_#{minutes}"]
if duration_enabled
enabledMinutes.push(minutes)
if !@isRecurring()
for minutes in enabledMinutes
lesson_price = teacher["price_per_lesson_#{minutes}_cents"]
value = "single|#{minutes}"
display = "#{minutes} Minute Lesson for $#{(lesson_price / 100).toFixed(2)}"
results.push(``)
else
for minutes in enabledMinutes
lesson_price = teacher["price_per_lesson_#{minutes}_cents"]
if lesson_price
value = "single|#{minutes}"
display = "#{minutes} Minute Lesson Each Week - $#{(lesson_price / 100).toFixed(2)} Per Week"
results.push(``)
for minutes in enabledMinutes
monthly_price = teacher["price_per_month_#{minutes}_cents"]
if monthly_price
value = "monthly|#{minutes}"
display = "#{minutes} Minute Lesson Each Week - $#{(monthly_price / 100).toFixed(2)} Per Month"
results.push(``)
if results.length == 0
results.push(``)
else
results.unshift(``)
results
render: () ->
teacher = @state.teacher
photo_url = teacher?.photo_url
if !photo_url?
photo_url = '/assets/shared/avatar_generic.png'
if teacher?
name = `
You are booking a single 30-minute TestDrive session.
You currently have {testDriveLessons} available. If you need to cancel, you must cancel at least 24 hours before the lesson is scheduled to start, or you will be charged 1 TestDrive lesson credit.
You are booking a single 30-minute TestDrive session.
Once payment is entered on the next screen, the teacher will be notified, and this lesson will then use 1 of {testDriveCredits} TestDrive credits. If you need to cancel, you must cancel at least 24 hours before the lesson is scheduled to start, or you will be charged 1 TestDrive lesson credit.