* get lesson booking screen cleaned up (better states at end)
This commit is contained in:
parent
397be9ab14
commit
a4f7e28acc
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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!
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
module TZInfo
|
||||
class Timezone
|
||||
def pretty_name
|
||||
name = tz.name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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!
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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|
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue