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() @setState({ recurring: value }) componentDidMount: () -> @checkboxes = [{selector: 'input.lesson-frequency', 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 @setState({teacher: response, isSelf: response.id == context.JK.currentUserId, school_on_school: school_on_school}) else logger.debug("BookLesson: ignoring teacher details", response.id, @state.teacherId) getInitialState: () -> { user: null, teacher: null, teacherId: null, generalErrors: null, descriptionErrors: null, bookedPriceErrors: null, slot1Errors: null, slot2Errors: null updating: false, recurring: 'single' } 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.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 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"] 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"] 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 = `
{teacher.name}
` teacher_first_name = teacher.first_name else name = `
Loading...
` teacher_first_name = '...' hours = [] for hour in ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'] if hour == '12' key = '00' else key = hour hours.push(``) minutes = [] for minute in ['00', '15', '30', '45'] minutes.push(``) am_pm = [``, ``] bookLessonClasses = classNames({"button-orange": true, 'book-lesson-btn': true, disabled: !this.state.teacher? && !@state.updating}) cancelClasses = classNames({"button-grey": true, disabled: !this.state.teacher? && !@state.updating}) descriptionErrors = context.JK.reactSingleFieldErrors('description', @state.descriptionErrors) bookedPriceErrors = context.JK.reactSingleFieldErrors('booked_price', @state.bookedPriceErrors) slot1Errors = context.JK.reactErrors(@state.slot1Errors, {preferred_day: 'Date', day_of_week: 'Day'}) slot2Errors = context.JK.reactErrors(@state.slot2Errors, {preferred_day: 'Date', day_of_week: 'Day'}) generalErrors = context.JK.reactErrors(@state.generalErrors, {user: 'You'}) bookedPriceClasses = classNames({booked_price: true, error: bookedPriceErrors?, field: true, 'booking-options': true}) descriptionClasses = classNames({description: true, error: descriptionErrors?}) slot1Classes = classNames({slot: true, 'slot-1': true, error: slot1Errors?}) slot2Classes = classNames({slot: true, 'slot-2': true, error: slot2Errors?}) generalClasses = classNames({general: true, error: generalErrors?}) if !@isRecurring() slots = `
What date/time do you prefer for your lesson?
:
{slot1Errors}
What is a second date/time option if preferred not available?
:
{slot2Errors}
` else days = [] days.push(``) days.push(``) days.push(``) days.push(``) days.push(``) days.push(``) days.push(``) days.push(``) slots = `
What day/time do you prefer for your lesson?
:
{slot1Errors}
What is a second day/time option if preferred not available?
:
{slot2Errors}
` if @isTestDrive() header = `

book testdrive lesson

` if @state.user?.remaining_test_drives == 1 testDriveLessons = "1 TestDrive lesson credit" else testDriveLessons = "#{this.state.user.remaining_test_drives} TestDrive lesson credits" actions = `
CANCEL BOOK TESTDRIVE LESSON
` testDriveCredits = 1 if this.state.user.lesson_package_type_id == 'test-drive' testDriveCredits = 4 else if this.state.user.lesson_package_type_id == 'test-drive-1' testDriveCredits = 1 else if this.state.user.lesson_package_type_id == 'test-drive-2' testDriveCredits = 2 if this.state.user.remaining_test_drives > 0 testDriveBookingInfo = `

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.

jamclass policies

` else testDriveBookingInfo = `

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.

jamclass policies

` columnLeft = `
{header} {slots}
Tell {teacher_first_name} a little about yourself as a student.