diff --git a/ruby/lib/jam_ruby.rb b/ruby/lib/jam_ruby.rb
index e2e86525e..d81652c2b 100755
--- a/ruby/lib/jam_ruby.rb
+++ b/ruby/lib/jam_ruby.rb
@@ -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"
diff --git a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/teacher_welcome_message.html.erb b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/teacher_welcome_message.html.erb
index 3b8a00d23..0590bfa1c 100644
--- a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/teacher_welcome_message.html.erb
+++ b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/teacher_welcome_message.html.erb
@@ -19,57 +19,41 @@
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:
-
-
-
- - Sign in to JamKazam using the email and password
- you used to register.
-
- - Edit your musician profile to
- describe yourself as a musician.
-
- -
- Edit your teacher
- profile to describe yourself as a teacher.
-
- - View
- your teacher profile to see how you will be presented to students.
- - 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.
2. Set Up Your Gear
- Use this link to a set of
- help
- articles on how to set up your gear 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. 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. 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 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 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
+ 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.
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
+ 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!
diff --git a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/teacher_welcome_message.text.erb b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/teacher_welcome_message.text.erb
index fb6468451..9702e24f5 100644
--- a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/teacher_welcome_message.text.erb
+++ b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/teacher_welcome_message.text.erb
@@ -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 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!
diff --git a/ruby/lib/jam_ruby/lib/timezone.rb b/ruby/lib/jam_ruby/lib/timezone.rb
new file mode 100644
index 000000000..a11a67eb5
--- /dev/null
+++ b/ruby/lib/jam_ruby/lib/timezone.rb
@@ -0,0 +1,8 @@
+
+module TZInfo
+ class Timezone
+ def pretty_name
+ name = tz.name
+ end
+ end
+end
\ No newline at end of file
diff --git a/ruby/lib/jam_ruby/models/lesson_booking_slot.rb b/ruby/lib/jam_ruby/models/lesson_booking_slot.rb
index e0c8e9efa..47a99b81c 100644
--- a/ruby/lib/jam_ruby/models/lesson_booking_slot.rb
+++ b/ruby/lib/jam_ruby/models/lesson_booking_slot.rb
@@ -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)
diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb
index aed3d83c1..f0764e082 100644
--- a/ruby/lib/jam_ruby/models/user.rb
+++ b/ruby/lib/jam_ruby/models/user.rb
@@ -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
diff --git a/ruby/spec/jam_ruby/flows/testdrive_lesson_spec.rb b/ruby/spec/jam_ruby/flows/testdrive_lesson_spec.rb
index 320ce3b5a..49f70058f 100644
--- a/ruby/spec/jam_ruby/flows/testdrive_lesson_spec.rb
+++ b/ruby/spec/jam_ruby/flows/testdrive_lesson_spec.rb
@@ -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
diff --git a/ruby/spec/support/lesson_session.rb b/ruby/spec/support/lesson_session.rb
index e21866767..e535b4686 100644
--- a/ruby/spec/support/lesson_session.rb
+++ b/ruby/spec/support/lesson_session.rb
@@ -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!
diff --git a/web/app/assets/javascripts/react-components/BookLesson.js.jsx.coffee b/web/app/assets/javascripts/react-components/BookLesson.js.jsx.coffee
index e003890b6..f0dee6c7e 100644
--- a/web/app/assets/javascripts/react-components/BookLesson.js.jsx.coffee
+++ b/web/app/assets/javascripts/react-components/BookLesson.js.jsx.coffee
@@ -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.
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.
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
diff --git a/web/app/assets/javascripts/react-components/LessonBooking.js.jsx.coffee b/web/app/assets/javascripts/react-components/LessonBooking.js.jsx.coffee
index e4e0d5b6d..737ba136c 100644
--- a/web/app/assets/javascripts/react-components/LessonBooking.js.jsx.coffee
+++ b/web/app/assets/javascripts/react-components/LessonBooking.js.jsx.coffee
@@ -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
`
+ 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 = `
+ {this.userHeader(this.displayableLesson().missedUser)}
+
This lesson was missed by the {this.displayableLesson().missedRole}.
+
`
+ else if @isSuspended()
+ whatNow = ``
+ else
+ summary = null
+ else if @isNow()
- if @isRecurring()
- detail = `Your {this.lessonDesc()} will take place each {this.slotTime(this.state.booking.default_slot)}
`
- else
- detail = `Your {this.lessonDesc()} will take place this {this.slotTime(this.state.booking.default_slot)}
`
+ if @studentMadeDefaultSlot()
+ message = this.slotMessage(this.state.booking.default_slot, 'accept')
- `
-
+ if @isRecurring()
+ detail = `
Your {this.lessonDesc()} will take place each {this.slotTime(this.state.booking.default_slot)}
`
+ else
+ detail = `
Your {this.lessonDesc()} will take place this {this.slotTime(this.state.booking.default_slot)}
`
+ summary = `
{this.userHeader(this.teacher())}
Has accepted your lesson request.
{detail}
{message}
-
-
+
`
+
+ whatNow = `
What Now?
-
We strongly recommending adding this lesson to your calendar now so you don't forget it!
-
You can do this manually today; we will soon add a easier way to do so automatically.
- {this.nextLessonSummary()}
-
-
+
You should join the lesson session as soon as possible. join session now
+ {this.nextLessonSummary()}
+
`
+ else if @isPast()
+ if @studentMadeDefaultSlot()
+ message = this.slotMessage(this.state.booking.default_slot, 'accept')
+
+ if @isRecurring()
+ detail = `
Your {this.lessonDesc()} will take place each {this.slotTime(this.state.booking.default_slot)}
`
+ else
+ detail = `
Your {this.lessonDesc()} will take place this {this.slotTime(this.state.booking.default_slot)}
`
+ summary = `
+ {this.userHeader(this.teacher())}
+
Has accepted your lesson request.
+ {detail}
+ {message}
+
`
+ else
+ decision = `
`
+
+ `
+ {summary}
+ {whatNow}
+ {decision}
`
+
renderStudentCanceled: () ->
@renderCanceled()
@@ -714,11 +810,53 @@ UserStore = context.UserStore
`
renderTeacherApproved: () ->
+ if @isCompleted()
+ if @isMissed()
+ whatNow = null
+ summary = `
+ {this.userHeader(this.displayableLesson().missedUser)}
+
This lesson was missed by the {this.displayableLesson().missedRole}.
+
`
+ else if @isSuspended()
+ whatNow = ``
+ else
+ summary = null
+ else if @isNow()
+
+ if @studentMadeDefaultSlot()
+ message = this.slotMessage(this.state.booking.default_slot, 'accept')
+
+ if @isRecurring()
+ detail = `Your {this.lessonDesc()} will take place each {this.slotTime(this.state.booking.default_slot)}
`
+ else
+ detail = `Your {this.lessonDesc()} will take place this {this.slotTime(this.state.booking.default_slot)}
`
+ summary = `
+ {this.userHeader(this.student())}
+
Is ready to take the lesson.
+ {detail}
+ {message}
+
`
+
+ whatNow = `
+
What Now?
+
You should join the lesson session as soon as possible. join session now
+ {this.nextLessonSummary()}
+
`
+ else if @isPast()
+
+ else
+ decision = ``
+
`
- {this.nextLessonSummaryWithAvatar()}
-
+ {summary}
+ {whatNow}
+ {decision}
`
+
renderTeacherCanceled: () ->
@renderCanceled()
diff --git a/web/app/assets/javascripts/react-components/LessonPayment.js.jsx.coffee b/web/app/assets/javascripts/react-components/LessonPayment.js.jsx.coffee
index f79a22bce..dd535db43 100644
--- a/web/app/assets/javascripts/react-components/LessonPayment.js.jsx.coffee
+++ b/web/app/assets/javascripts/react-components/LessonPayment.js.jsx.coffee
@@ -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.
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.
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"
diff --git a/web/app/assets/javascripts/react-components/mixins/PostProcessorMixin.js.coffee b/web/app/assets/javascripts/react-components/mixins/PostProcessorMixin.js.coffee
index 11ff2e2ee..d7fd4134f 100644
--- a/web/app/assets/javascripts/react-components/mixins/PostProcessorMixin.js.coffee
+++ b/web/app/assets/javascripts/react-components/mixins/PostProcessorMixin.js.coffee
@@ -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)
diff --git a/web/app/views/api_lesson_bookings/show.rabl b/web/app/views/api_lesson_bookings/show.rabl
index f0e6f766c..611962dab 100644
--- a/web/app/views/api_lesson_bookings/show.rabl
+++ b/web/app/views/api_lesson_bookings/show.rabl
@@ -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
diff --git a/web/app/views/api_lesson_sessions/show.rabl b/web/app/views/api_lesson_sessions/show.rabl
index f10b404fd..8a6c872a6 100644
--- a/web/app/views/api_lesson_sessions/show.rabl
+++ b/web/app/views/api_lesson_sessions/show.rabl
@@ -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|
{
diff --git a/web/spec/features/lesson_booking_status_spec.rb b/web/spec/features/lesson_booking_status_spec.rb
new file mode 100644
index 000000000..86a947e00
--- /dev/null
+++ b/web/spec/features/lesson_booking_status_spec.rb
@@ -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
\ No newline at end of file
diff --git a/web/spec/support/lessons.rb b/web/spec/support/lessons.rb
index cc497ebf9..823588063 100644
--- a/web/spec/support/lessons.rb
+++ b/web/spec/support/lessons.rb
@@ -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