* get lesson booking screen cleaned up (better states at end)

This commit is contained in:
Seth Call 2016-05-19 13:26:37 -05:00
parent 397be9ab14
commit a4f7e28acc
16 changed files with 538 additions and 132 deletions

View File

@ -25,6 +25,7 @@ require 'stripe'
require 'zip-codes'
require 'email_validator'
require "jam_ruby/lib/timezone"
require "jam_ruby/constants/limits"
require "jam_ruby/constants/notification_types"
require "jam_ruby/constants/validation_messages"

View File

@ -19,57 +19,41 @@
<p><b style="color: white">1. Set Up Your Teacher Profile</b><br/>
As JamKazam brings students into the JamClass marketplace, these students will search for teachers. The way they find
As JamKazam brings students into the JamClass marketplace, these students search for teachers. The way they find
teachers is by searching on their criteria (e.g. instruments, genres, etc.), and then by browsing through teacher
profiles to get a feel for the teachers who match their search criteria. Your teacher profile is critical to being
found in searches, and then presenting yourself in more depth to students who are interested in you. So you'll want to
take a little time to fill in the information in your teacher profile to present yourself well.
<br/>
<br/>
To do this:
<br/>
<br/>
<ol>
<li><a href="https://www.jamkazam.com/signin" style="color:#fc0">Sign in to JamKazam</a> using the email and password
you used to register.
</li>
<li><a href="https://www.jamkazam.com/client#/account/profile" style="color:#fc0">Edit your musician profile</a> to
describe yourself as a musician.
</li>
<li>
<a href="https://www.jamkazam.com/client#/teachers/setup/introduction" style="color:#fc0">Edit your teacher
profile</a> to describe yourself as a teacher.
</li>
<li><a href="https://www.jamkazam.com/client#/profile/teacher/<%= @user.teacher.id %>" style="color:#fc0">View
your teacher profile</a> to see how you will be presented to students.</li>
<li>If you don't like anything about your teacher profile, use the edit link above to go back in and edit your
information as you like.
</li>
</ol>
<a style="color:#fc0" href="https://jamkazam.desk.com/customer/en/portal/articles/2405835-creating-your-teacher-profile">Click
here for
instructions on filling out your teacher profile</a>.
</p>
<p><b style="color: white">2. Set Up Your Gear</b><br/>
Use this link to a set of
<a href="https://jamkazam.desk.com/customer/en/portal/topics/673197-first-time-setup/articles" style="color:#fc0">help
articles on how to set up your gear</a> to be ready to teach online. After you have signed
up, someone from JamKazam will contact you to schedule a test online session, in which we will make sure your audio
and video gear are working properly in an online session, and to make sure you feel comfortable with the key features
you will be using in sessions with students.
<a style="color:#fc0" href="https://jamkazam.desk.com/customer/en/portal/articles/1288274-computer-internet-audio-and-video-requirements">Click
here for information on the gear requirements to effectively teach using the JamClass service</a>. When you have
everything you need,
<a style="color:#fc0" href="https://jamkazam.desk.com/customer/en/portal/topics/930331-setting-up-your-gear-to-play-in-online-sessions/articles">use
this set of help articles as a good step-by-step guide to set up your gear for use with the
JamKazam application</a>. After you have signed up, someone from JamKazam will contact you to schedule a test online
session, in which we will make sure your audio and video gear are working properly in an online session, and to make
sure you feel comfortable with the key features you will be using in sessions with students.
</p>
<p><b style="color: white">3. Learn About JamClass Features</b><br/>
Use this link to a set of
<a href="https://jamkazam.desk.com/customer/en/portal/topics/926076-jamclass-online-music-lessons---for-teachers/articles" style="color:#fc0">help
articles for teachers on JamClass</a> to familiarize yourself with the most useful features
for teaching students online. This includes how to respond to and book lessons requested by students, how to join your
students in online lessons, features you can use while in lessons, and much more. There is very important basic
information, plus some really nifty stuff here, so be sure to look through it at least briefly to see how we can
turbocharge your online lessons!
<a style="color:#fc0" href="https://jamkazam.desk.com/customer/en/portal/topics/926076-jamclass-online-music-lessons---for-teachers/articles">Click
this link for a set of help articles specifically for teachers</a> to learn how to respond to student lesson
requests, how to join your lessons when they are scheduled to begin, how to get paid, and more. You can also
<a style="color:#fc0" href="https://jamkazam.desk.com/customer/en/portal/topics/673198-key-features-to-use-in-online-sessions/articles">use
this
link for a set of help articles that explain how to use the key features available to you in online sessions</a> to
effectively teach students.
</p>
<p>
As you work through these things, if you ever get stuck or have questions, please don't hesitate to reach out for
help. You can email us any time at <a href="mailto:support@jamkazam.com" style="color:#fc0">support@jamkazam.com</a>. We are happy to help you, and we look forward to helping you
help. You can email us any time at <a href="mailto:support@jamkazam.com" style="color:#fc0">support@jamkazam.com</a>.
We are happy to help you, and we look forward to helping you
reach and teach more students!
</p>

View File

@ -9,28 +9,27 @@ Skype, etc.
To get ready to teach JamClass students online, here are the things you'll want to do:
1. Set Up Your Teacher Profile
As JamKazam brings students into the JamClass marketplace, these students will search for teachers. The way they find
As JamKazam brings students into the JamClass marketplace, these students search for teachers. The way they find
teachers is by searching on their criteria (e.g. instruments, genres, etc.), and then by browsing through teacher
profiles to get a feel for the teachers who match their search criteria. Your teacher profile is critical to being
found in searches, and then presenting yourself in more depth to students who are interested in you. So you'll want to
take a little time to fill in the information in your teacher profile to present yourself well.
To do this:
1. Sign in to JamKazam (https://www.jamkazam.com/signin) using the email and password you used to register.
2. Edit your musician profile (https://www.jamkazam.com/client#/account/profile) describe yourself as a musician.
3. Edit your teacher profile (https://www.jamkazam.com/client#/teachers/setup/introduction) to describe yourself as a teacher.
4. View your teacher profile (https://www.jamkazam.com/client#/profile/teacher/<%= @user.teacher.id %>) to see how you will be presented to students.
5. If you don't like anything about your teacher profile, use the edit link above to go back in and edit your information as you like.
Click here for instructions on filling out your teacher profile. (https://jamkazam.desk.com/customer/en/portal/articles/2405835-creating-your-teacher-profile)
2. Set Up Your Gear
Use this link to a set of help articles on how to set up your gear (https://jamkazam.desk.com/customer/en/portal/topics/673197-first-time-setup/articles)
to be ready to teach online. After you have signed up, someone from JamKazam will contact you to schedule a test online session, in which we will make sure your audio
and video gear are working properly in an online session, and to make sure you feel comfortable with the key features
you will be using in sessions with students.
Click here for information on the gear requirements to effectively teach using the JamClass service (https://jamkazam.desk.com/customer/en/portal/articles/1288274-computer-internet-audio-and-video-requirements).
When you have everything you need, use
this set of help articles as a good step-by-step guide to set up your gear for use with the
JamKazam application (https://jamkazam.desk.com/customer/en/portal/topics/930331-setting-up-your-gear-to-play-in-online-sessions/articles).
After you have signed up, someone from JamKazam will contact you to schedule a test online
session, in which we will make sure your audio and video gear are working properly in an online session, and to make
sure you feel comfortable with the key features you will be using in sessions with students.
3. Learn About JamClass Features
Use this link to a set of help articles for teachers on JamClass (https://jamkazam.desk.com/customer/en/portal/topics/926076-jamclass-online-music-lessons---for-teachers/articles) to familiarize yourself with the most useful features for teaching students online. This includes how to respond to and book lessons requested by students, how to join your students in online lessons, features you can use while in lessons, and much more. There is very important basic information, plus some really nifty stuff here, so be sure to look through it at least briefly to see how we can turbocharge your online lessons!
Click this link for a set of help articles specifically for teachers</a> to learn how to respond to student lesson
requests, how to join your lessons when they are scheduled to begin, how to get paid, and more (https://jamkazam.desk.com/customer/en/portal/topics/926076-jamclass-online-music-lessons---for-teachers/articles).
You can also use this link for a set of help articles that explain how to use the key features available to you in online sessions to
effectively teach students (https://jamkazam.desk.com/customer/en/portal/topics/673198-key-features-to-use-in-online-sessions/articles).
As you work through these things, if you ever get stuck or have questions, please don't hesitate to reach out for help. You can email us any time at support@jamkazam.com. We are happy to help you, and we look forward to helping you reach and teach more students!

View File

@ -0,0 +1,8 @@
module TZInfo
class Timezone
def pretty_name
name = tz.name
end
end
end

View File

@ -179,6 +179,13 @@ module JamRuby
end
end
def pretty_timezone
begin
tz = TZInfo::Timezone.get(timezone)
rescue Exception => e
@@log.error("unable to find timezone=#{tz_identifier}, e=#{e}")
end
end
def pretty_start_time(with_timezone = true)
start_time = scheduled_time(0)

View File

@ -2092,15 +2092,20 @@ module JamRuby
def test_drive_declined(lesson_session)
# because we decrement test_drive credits as soon as you book, we need to bring it back now
if lesson_session.lesson_booking.user_decremented
self.remaining_test_drives = self.remaining_test_drives + 1
self.save(validate: false)
end
end
def test_drive_failed(lesson_session)
# because we decrement test_drive credits as soon as you book, we need to bring it back now
self.remaining_test_drives = self.remaining_test_drives + 1
self.save(validate: false)
if lesson_session.lesson_booking.user_decremented
# because we decrement test_drive credits as soon as you book, we need to bring it back now
self.remaining_test_drives = self.remaining_test_drives + 1
self.save(validate: false)
end
UserMailer.student_test_drive_no_bill(lesson_session).deliver
end

View File

@ -235,4 +235,40 @@ describe "TestDrive Lesson Flow" do
sale.sale_line_items.count.should eql 1
sale.sale_line_items[0].affiliate_distributions.count.should eql 0
end
# VRFS-4069
it "cancels with no credit card " do
slots = []
slots << FactoryGirl.build(:lesson_booking_slot_single)
slots << FactoryGirl.build(:lesson_booking_slot_single)
booking = LessonBooking.book_test_drive(user, teacher_user, slots, "Hey I've heard of you before.")
booking.errors.any?.should be_false
lesson = booking.lesson_sessions[0]
booking.card_presumed_ok.should be_false
user.reload
user.remaining_test_drives.should eql 0
lesson.cancel({canceler: user, message: "sorry about that"})
user.reload
user.remaining_test_drives.should eql 0
end
it "cancels with credit card " do
lesson = testdrive_lesson(user, teacher_user)
user.reload
user.remaining_test_drives.should eql 3
lesson.cancel({canceler: user, message: "sorry about that"})
user.reload
user.remaining_test_drives.should eql 4
end
end

View File

@ -11,7 +11,7 @@ module StripeMock
end
end
def testdrive_lesson(user, teacher, finish = false)
def testdrive_lesson(user, teacher, options = {finish: false, accept: true, cancel: false, miss: false})
#if slots.nil?
slots = []
@ -24,7 +24,6 @@ def testdrive_lesson(user, teacher, finish = false)
user.save!
end
booking = LessonBooking.book_test_drive(user, teacher, slots, "Hey I've heard of you before.")
if booking.errors.any?
puts "BOOKING #{booking.errors.inspect}"
@ -32,23 +31,40 @@ def testdrive_lesson(user, teacher, finish = false)
booking.errors.any?.should be_false
lesson = booking.lesson_sessions[0]
start = lesson.scheduled_start
end_time = lesson.scheduled_start + (60 * lesson.duration)
booking.card_presumed_ok.should be_true
if user.most_recent_test_drive_purchase.nil?
LessonPackagePurchase.create(user, booking, LessonPackageType.test_drive_4)
end
lesson.accept({message: 'Yeah I got this', slot: slots[0]})
lesson.errors.any?.should be_false
lesson.reload
lesson.slot.should eql slots[0]
lesson.status.should eql LessonSession::STATUS_APPROVED
if options[:accept]
lesson.accept({message: 'Yeah I got this', slot: slots[0]})
lesson.errors.any?.should be_false
lesson.reload
lesson.slot.should eql slots[0]
lesson.status.should eql LessonSession::STATUS_APPROVED
end
if finish
if options[:cancel]
lesson.cancel({canceler: options[:canceler] || user, message: "sorry about that"})
lesson.reload
lesson.status.should eql LessonSession::STATUS_CANCELED
end
if options[:miss]
# teacher & student get into session
start = lesson.scheduled_start
end_time = lesson.scheduled_start + (60 * lesson.duration)
uh2 = FactoryGirl.create(:music_session_user_history, user: teacher_user, history: lesson.music_session, created_at: start, session_removed_at: end_time)
Timecop.travel(end_time + 1)
lesson.analyse
lesson.session_completed
els if options[:finish]
# teacher & student get into session
uh2 = FactoryGirl.create(:music_session_user_history, user: teacher, history: lesson.music_session, created_at: start, session_removed_at: end_time)
# artificially end the session, which is covered by other background jobs
lesson.music_session.session_removed_at = end_time
lesson.music_session.save!

View File

@ -228,7 +228,7 @@ UserStore = context.UserStore
@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.<br/><br/>We've taken you automatically to the page for this request, and sent an email to you with a link here as well. All communication with the teacher will show up on this page and in email.")
context.JK.Banner.showNotice("Lesson Requested","The teacher has been notified of your lesson request, and should respond soon.<br/><br/>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

View File

@ -7,6 +7,7 @@ UserStore = context.UserStore
@LessonBooking = React.createClass({
mixins: [
@PostProcessorMixin,
Reflux.listenTo(AppStore, "onAppInit"),
Reflux.listenTo(UserStore, "onUserChanged")
]
@ -97,6 +98,13 @@ UserStore = context.UserStore
update_all = !booking.focused_lesson?.id?
if booking.focused_lesson?
#booking.focused_lesson.lesson_booking = booking
@postProcessLesson(booking.focused_lesson)
if booking.next_lesson?
#booking.next_lesson.lesson_booking = booking
@postProcessLesson(booking.next_lesson)
@setState({booking: booking, updating: false, slot_decision: startSlotDecision, updatingLesson: false, update_all: update_all})
getLessonBookingDone: (response) ->
@ -286,7 +294,7 @@ UserStore = context.UserStore
@student()?.id == @defaultSlot()?.proposer_id
teacherViewing: () ->
@state.booking? && @state.booking.teacher_id == context.JK.currentUserId
!@studentViewing()
studentViewing: () ->
@state.booking? && @state.booking.user_id == context.JK.currentUserId
@ -297,6 +305,9 @@ UserStore = context.UserStore
isRequested: () ->
@state.booking?.status == 'requested' && !@isCounter()
isSuccessful: () ->
@displayableLesson().success
isCounter: () ->
@counteredSlot()? && !@isCanceled() && !@isSuspended()
@ -306,6 +317,12 @@ UserStore = context.UserStore
isActiveCounter: () ->
@isCounter() && @isActive()
isCompleted: () ->
if @state.booking?
@displayableLesson().status == 'completed'
else
false
isApproved: () ->
@state.booking?.status == 'approved'
@ -419,7 +436,7 @@ UserStore = context.UserStore
hour = 12
am_pm = 'am'
"#{context.JK.padString(hour.toString(), 2)}:#{context.JK.padString(slot.minute.toString(), 2)}#{am_pm} (#{slot.timezone})"
"#{context.JK.padString(hour.toString(), 2)}:#{context.JK.padString(slot.minute.toString(), 2)}#{am_pm} (#{slot.pretty_timezone})"
displayableLesson: () ->
lesson = @focusedLesson()
@ -449,6 +466,9 @@ UserStore = context.UserStore
isPast: () ->
new Date().getTime() > new Date(@displayableLesson().scheduled_start).getTime()
isMissed: () ->
@displayableLesson().missed
sessionLink: () ->
link = "/client#/session/#{this.displayableLesson().music_session_id}"
@ -552,13 +572,42 @@ UserStore = context.UserStore
return @renderLoading()
completedHeader: () ->
if @isNow()
header = 'the lesson is scheduled for right now!'
else if @isPast()
if @isMissed()
header = "this lesson was #{this.displayableLesson().displayStatus.toLowerCase()}"
else if @isSuspended()
header = 'this lesson was suspended due to billing issues'
else
header = 'this lesson is over'
else
header = 'this lesson is over'
header
approvedHeader: () ->
if @isCompleted()
if @isMissed()
header = "this lesson was #{this.displayableLesson().displayStatus.toLowerCase()}"
else if @isSuspended()
header = 'this lesson was suspended due to billing issues'
else
header = 'this lesson is over'
else if @isNow()
header = 'the lesson is scheduled for right now!'
else if @isPast()
header = 'this lesson is over'
else
header = 'this lesson is coming up soon'
header
renderTeacher: () ->
if @isRequested()
header = 'respond to lesson request'
content = @renderTeacherRequested()
if @isCounter()
else if @isCounter()
if @isTeacherCountered()
header = 'your proposed alternate day/time is still pending'
else
@ -566,17 +615,16 @@ UserStore = context.UserStore
content = @renderTeacherCountered()
else if @isApproved()
if @isNow()
header = 'the lesson is scheduled for right now!'
else if @isPast()
header = 'this lesson is over'
else
header = 'this lesson is coming up soon'
header = @approvedHeader()
content = @renderTeacherApproved()
else if @isCompleted()
header = @completedHeader()
content = @renderTeacherComplete()
else if @isCanceled()
header = 'this lesson is canceled'
header = "this lesson was #{this.displayableLesson()?.displayStatus?.toLowerCase()}"
content = @renderTeacherCanceled()
else if @isSuspended()
@ -600,7 +648,7 @@ UserStore = context.UserStore
header = 'your lesson has been requested'
content = @renderStudentRequested()
if @isCounter()
else if @isCounter()
if @isTeacherCountered()
header = 'teacher has proposed an alternate day/time'
else
@ -608,20 +656,19 @@ UserStore = context.UserStore
content = @renderTeacherCountered()
else if @isApproved()
if @isNow()
header = 'the lesson is scheduled for right now!'
else if @isPast()
header = 'this lesson is over'
else
header = 'this lesson is coming up soon'
header = @approvedHeader()
content = @renderStudentApproved()
else if @isCompleted()
header = @completedHeader()
content = @renderStudentComplete()
else if @isCanceled()
if @neverAccepted() && @studentViewing() && !@studentCanceled()
header = "we're sorry, but your lesson request has been declined"
else
header = 'this lesson is canceled'
header = "this lesson was #{this.displayableLesson()?.displayStatus?.toLowerCase()}"
content = @renderStudentCanceled()
@ -660,32 +707,81 @@ UserStore = context.UserStore
</div>
</div>`
joinSessionNow: (e) ->
e.preventDefault()
SessionActions.enterSession(@displayableLesson().music_session.id)
updateCreditCard: (e) ->
window.location.href="/client#/account/paymentHistory"
renderStudentComplete: () ->
@renderStudentApproved()
renderTeacherComplete: () ->
@renderTeacherApproved()
renderStudentApproved: () ->
if @studentMadeDefaultSlot()
message = this.slotMessage(this.state.booking.default_slot, 'accept')
if @isCompleted()
if @isMissed()
whatNow = null
summary = `<div className="row">
{this.userHeader(this.displayableLesson().missedUser)}
<p>This lesson was missed by the {this.displayableLesson().missedRole}.</p>
</div>`
else if @isSuspended()
whatNow = `<div className="row">
<h3>What Now?</h3>
<p>You should update your credit card info. <a onClick={this.updateCreditCard}>update credit card</a></p>
</div>`
else
summary = null
else if @isNow()
if @isRecurring()
detail = `<p>Your {this.lessonDesc()} will take place each {this.slotTime(this.state.booking.default_slot)}</p>`
else
detail = `<p>Your {this.lessonDesc()} will take place this {this.slotTime(this.state.booking.default_slot)}</p>`
if @studentMadeDefaultSlot()
message = this.slotMessage(this.state.booking.default_slot, 'accept')
`<div className="contents">
<div className="row">
if @isRecurring()
detail = `<p>Your {this.lessonDesc()} will take place each {this.slotTime(this.state.booking.default_slot)}</p>`
else
detail = `<p>Your {this.lessonDesc()} will take place this {this.slotTime(this.state.booking.default_slot)}</p>`
summary = `<div className="row">
{this.userHeader(this.teacher())}
<p>Has accepted your lesson request.</p>
{detail}
{message}
</div>
<div className="row">
</div>`
whatNow = `<div className="row">
<h3>What Now?</h3>
<p>We strongly recommending adding this lesson to your calendar now so you don't forget it!</p>
<p>You can do this manually today; we will soon add a easier way to do so automatically.</p>
{this.nextLessonSummary()}
</div>
<LessonBookingDecision {...this.decisionProps([])} />
<p>You should join the lesson session as soon as possible. <a onClick={this.joinSessionNow}>join session now</a></p>
{this.nextLessonSummary()}
</div>`
else if @isPast()
if @studentMadeDefaultSlot()
message = this.slotMessage(this.state.booking.default_slot, 'accept')
if @isRecurring()
detail = `<p>Your {this.lessonDesc()} will take place each {this.slotTime(this.state.booking.default_slot)}</p>`
else
detail = `<p>Your {this.lessonDesc()} will take place this {this.slotTime(this.state.booking.default_slot)}</p>`
summary = `<div className="row">
{this.userHeader(this.teacher())}
<p>Has accepted your lesson request.</p>
{detail}
{message}
</div>`
else
decision = `<LessonBookingDecision {...this.decisionProps([])} />`
`<div className="contents">
{summary}
{whatNow}
{decision}
</div>`
renderStudentCanceled: () ->
@renderCanceled()
@ -714,11 +810,53 @@ UserStore = context.UserStore
</div>`
renderTeacherApproved: () ->
if @isCompleted()
if @isMissed()
whatNow = null
summary = `<div className="row">
{this.userHeader(this.displayableLesson().missedUser)}
<p>This lesson was missed by the {this.displayableLesson().missedRole}.</p>
</div>`
else if @isSuspended()
whatNow = `<div className="row">
<h3>What Now?</h3>
<p>You should update your credit card info. <a onClick={this.updateCreditCard}>update credit card</a></p>
</div>`
else
summary = null
else if @isNow()
if @studentMadeDefaultSlot()
message = this.slotMessage(this.state.booking.default_slot, 'accept')
if @isRecurring()
detail = `<p>Your {this.lessonDesc()} will take place each {this.slotTime(this.state.booking.default_slot)}</p>`
else
detail = `<p>Your {this.lessonDesc()} will take place this {this.slotTime(this.state.booking.default_slot)}</p>`
summary = `<div className="row">
{this.userHeader(this.student())}
<p>Is ready to take the lesson.</p>
{detail}
{message}
</div>`
whatNow = `<div className="row">
<h3>What Now?</h3>
<p>You should join the lesson session as soon as possible. <a onClick={this.joinSessionNow}>join session now</a></p>
{this.nextLessonSummary()}
</div>`
else if @isPast()
else
decision = `<LessonBookingDecision {...this.decisionProps([])} />`
`<div className="contents">
{this.nextLessonSummaryWithAvatar()}
<LessonBookingDecision {...this.decisionProps([])} />
{summary}
{whatNow}
{decision}
</div>`
renderTeacherCanceled: () ->
@renderCanceled()

View File

@ -327,7 +327,7 @@ UserStore = context.UserStore
window.location = "/client#/jamclass/"
else if response.lesson?.id?
context.JK.Banner.showNotice("Lesson Requested","The teacher has been notified of your lesson request, and should respond soon.<br/><br/>We've taken you automatically to the page for this request, and sent an email to you with a link here as well. All communication with the teacher will show up on this page and in email.")
context.JK.Banner.showNotice("Lesson Requested","The teacher has been notified of your lesson request, and should respond soon.<br/><br/>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.lesson.id
url = "/client#/jamclass"

View File

@ -5,7 +5,7 @@ teacherActions = window.JK.Actions.Teacher
postProcessLesson: (lesson) ->
if lesson.music_session.user_id == context.JK.currentUserId
if lesson.student_id == context.JK.currentUserId
me = lesson.student
other = lesson.teacher
lesson.hasUnreadMessages = lesson['student_unread_messages']
@ -33,13 +33,30 @@ teacherActions = window.JK.Actions.Teacher
if !lesson.displayStatus?
if lesson.status == 'canceled'
lesson.displayStatus = 'Canceled'
if lesson.student_canceled
lesson.displayStatus = 'Canceled (Student)'
else if lesson.teacher_canceled
lesson.displayStatus = 'Canceled (Teacher)'
else if lesson.status == 'suspended'
lesson.displayStatus = 'Suspended'
else
if lesson.success
lesson.displayStatus = 'Completed'
else
lesson.displayStatus = 'Missed'
lesson.missed = true
lesson.missedRole = 'teacher'
lesson.missedUser = lesson.teacher
if lesson.analysis?.reason == 'teacher_fault'
lesson.missedRole = 'teacher'
lesson.missedUser = lesson.teacher
lesson.displayStatus = 'Missed (Teacher)'
else if lesson.analysis?.reason == 'student_fault'
lesson.displayStatus = 'Missed (Student)'
lesson.missedRole = 'student'
lesson.missedUser = lesson.student
else
lesson.displayStatus = 'Missed'
@postProcessUser(me)
@postProcessUser(other)

View File

@ -27,19 +27,12 @@ node :teacher do |lesson_booking|
partial "api_users/show", object: lesson_booking.teacher
end
child(:next_lesson => :next_lesson) do |next_lesson|
attributes :id, :scheduled_start, :status, :music_session_id, :pretty_scheduled_start, :teacher_short_canceled
node :next_lesson do |lesson_booking|
partial "api_lesson_sessions/show", object: lesson_booking.next_lesson
end
if @lesson_session
node :focused_lesson do
{
id: @lesson_session.id,
scheduled_start: @lesson_session.scheduled_start,
status: @lesson_session.status,
music_session_id: @lesson_session.music_session.id,
pretty_scheduled_start: @lesson_session.pretty_scheduled_start,
teacher_short_canceled: @lesson_session.teacher_short_canceled
}
partial "api_lesson_sessions/show", object: @lesson_session
end
end

View File

@ -2,7 +2,8 @@ object @lesson_session
attributes :id, :lesson_booking_id, :lesson_type, :duration, :price, :teacher_complete, :student_complete,
:status, :student_canceled, :teacher_canceled, :student_canceled_at, :teacher_canceled_at, :student_canceled_reason,
:teacher_canceled_reason, :status, :success, :teacher_unread_messages, :student_unread_messages, :is_active?, :recurring, :analysed, :school_on_school?
:teacher_canceled_reason, :status, :success, :teacher_unread_messages, :student_unread_messages, :is_active?, :recurring,
:analysed, :school_on_school?, :teacher_id, :student_id, :pretty_scheduled_start, :scheduled_start, :teacher_short_canceled
node do |lesson_session|
{

View File

@ -0,0 +1,169 @@
require 'spec_helper'
describe "Lesson Booking Status page", :js => true, :type => :feature, :capybara_feature => true do
subject { page }
let(:user) { FactoryGirl.create(:user) }
let(:teacher) { FactoryGirl.create(:teacher_user) }
after(:each) do
Timecop.return
end
def screenshot
if ENV['SCREENSHOT'] == '1'
screenshot_and_save_page
end
end
describe "student" do
it "requested" do
lesson = testdrive_lesson(user, teacher, {accept:false, finish:false})
lesson.lesson_booking.status.should eql LessonBooking::STATUS_REQUESTED
fast_signin(user, "/client#/jamclass/lesson-booking/" + lesson.id)
find('h2', text: 'your lesson has been requested')
screenshot
end
it "approved" do
lesson = testdrive_lesson(user, teacher, {accept:true, finish:false})
fast_signin(user, "/client#/jamclass/lesson-booking/" + lesson.id)
find('h2', text: 'this lesson is coming up soon')
screenshot
end
it "now" do
pending "sinon needed to fake time"
lesson = testdrive_lesson(user, teacher, {accept:true, finish:false})
Timecop.travel(lesson.scheduled_start + 1)
fast_signin(user, "/client#/jamclass/lesson-booking/" + lesson.id)
find('h2', text: 'the lesson is scheduled for right now!')
screenshot
end
it "successful" do
lesson = testdrive_lesson(user, teacher, {accept:true, finish:true})
# travel to after the lesson is over
Timecop.travel(lesson.scheduled_start + (lesson.duration * 60) + 1)
lesson.reload
lesson.success.should be_true
lesson.status.should eql LessonSession::STATUS_COMPLETED
fast_signin(user, "/client#/jamclass/lesson-booking/" + lesson.id)
find('h2', text: 'this lesson is over')
screenshot
end
it "canceled" do
lesson = testdrive_lesson(user, teacher, {accept:true, finish:false, cancel: true})
lesson.reload
lesson.status.should eql LessonSession::STATUS_CANCELED
fast_signin(user, "/client#/jamclass/lesson-booking/" + lesson.id)
find('h2', text: "this lesson was canceled (student)")
screenshot
end
it "missed" do
lesson = testdrive_lesson(user, teacher, {accept:true, finish:false, miss: true})
fast_signin(user, "/client#/jamclass/lesson-booking/" + lesson.id)
find('h2', text: "this lesson was missed (teacher)")
screenshot
end
end
describe "teacher" do
it "requested" do
lesson = testdrive_lesson(user, teacher, {accept:false, finish:false})
fast_signin(teacher, "/client#/jamclass/lesson-booking/" + lesson.id)
find('h2', text: 'respond to lesson request')
screenshot
end
it "approved" do
lesson = testdrive_lesson(user, teacher, {accept:true, finish:false})
fast_signin(teacher, "/client#/jamclass/lesson-booking/" + lesson.id)
find('h2', text: 'this lesson is coming up soon')
screenshot
end
it "now" do
pending "sinon needed to fake time"
lesson = testdrive_lesson(user, teacher, {accept:true, finish:false})
Timecop.travel(lesson.scheduled_start + 1)
fast_signin(teacher, "/client#/jamclass/lesson-booking/" + lesson.id)
find('h2', text: 'the lesson is scheduled for right now!')
screenshot
end
it "successful" do
lesson = testdrive_lesson(user, teacher, {accept:true, finish:true})
# travel to after the lesson is over
Timecop.travel(lesson.scheduled_start + (lesson.duration * 60) + 1)
lesson.success.should be_true
lesson.status.should eql LessonSession::STATUS_COMPLETED
fast_signin(teacher, "/client#/jamclass/lesson-booking/" + lesson.id)
find('h2', text: 'this lesson is over')
screenshot
end
it "canceled" do
lesson = testdrive_lesson(user, teacher, {accept:true, finish:false, cancel: true})
fast_signin(teacher, "/client#/jamclass/lesson-booking/" + lesson.id)
find('h2', text: "this lesson was canceled (student)")
screenshot
end
it "missed" do
lesson = testdrive_lesson(user, teacher, {accept:true, finish:false, miss: true})
fast_signin(teacher, "/client#/jamclass/lesson-booking/" + lesson.id)
find('h2', text: "this lesson was missed (teacher)")
screenshot
end
end
end

View File

@ -84,37 +84,69 @@ def create_stripe_token(exp_month = 2017)
end
def testdrive_lesson(user, teacher, slots = nil)
if slots.nil?
slots = []
slots << FactoryGirl.build(:lesson_booking_slot_single)
slots << FactoryGirl.build(:lesson_booking_slot_single)
end
def testdrive_lesson(user, teacher, options = {finish: false, accept: true, cancel: false, miss: false})
if !user.stored_credit_card
token = create_stripe_token
user.payment_update({token: token, zip: '78759'})
#if slots.nil?
slots = []
slots << FactoryGirl.build(:lesson_booking_slot_single)
slots << FactoryGirl.build(:lesson_booking_slot_single)
#end
if user.stored_credit_card == false
user.stored_credit_card = true
user.save!
user.stored_credit_card.should be_true
end
booking = LessonBooking.book_test_drive(user, teacher, slots, "Hey I've heard of you before.")
#puts "BOOKING #{booking.errors.inspect}"
if booking.errors.any?
puts "BOOKING #{booking.errors.inspect}"
end
booking.errors.any?.should be_false
lesson = booking.lesson_sessions[0]
start = lesson.scheduled_start
end_time = lesson.scheduled_start + (60 * lesson.duration)
booking.card_presumed_ok.should be_true
if user.most_recent_test_drive_purchase.nil?
LessonPackagePurchase.create(user, booking, LessonPackageType.test_drive_4)
end
lesson.accept({message: 'Yeah I got this', slot: slots[0]})
lesson.errors.any?.should be_false
lesson.reload
lesson.slot.should eql slots[0]
lesson.status.should eql LessonSession::STATUS_APPROVED
if options[:accept]
lesson.accept({message: 'Yeah I got this', slot: slots[0]})
lesson.errors.any?.should be_false
lesson.reload
lesson.slot.should eql slots[0]
lesson.status.should eql LessonSession::STATUS_APPROVED
end
if options[:cancel]
lesson.cancel({canceler: options[:canceler] || user, message: "sorry about that"})
lesson.reload
lesson.status.should eql LessonSession::STATUS_CANCELED
end
if options[:miss]
# teacher & student get into session
Timecop.travel(end_time + 1)
lesson.analyse
lesson.session_completed
elsif options[:finish]
# teacher & student get into session
uh2 = FactoryGirl.create(:music_session_user_history, user: teacher, history: lesson.music_session, created_at: start, session_removed_at: end_time)
# artificially end the session, which is covered by other background jobs
lesson.music_session.session_removed_at = end_time
lesson.music_session.save!
Timecop.travel(end_time + 1)
lesson.analyse
lesson.session_completed
end
lesson
end