Merge branch 'develop' into feature/packages
This commit is contained in:
commit
1abacf0ec6
|
|
@ -1,3 +1,4 @@
|
||||||
|
|
||||||
#!/usr/bin/env rake
|
#!/usr/bin/env rake
|
||||||
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
||||||
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
ActiveAdmin.register JamRuby::User, :as => 'UserSource' do
|
||||||
|
|
||||||
|
menu :label => 'User Campaigns', :parent => 'Users'
|
||||||
|
|
||||||
|
config.sort_order = 'created_at DESC'
|
||||||
|
config.batch_actions = false
|
||||||
|
config.clear_action_items!
|
||||||
|
config.filters = false
|
||||||
|
|
||||||
|
scope("Most Recent First", default: true) { |scope| scope.unscoped.order('created_at desc')}
|
||||||
|
|
||||||
|
index do
|
||||||
|
column "Email" do |user|
|
||||||
|
user.email
|
||||||
|
end
|
||||||
|
column "Bought TestDrive" do |user|
|
||||||
|
!user.most_recent_test_drive_purchase.nil? ? "Yes" : "No"
|
||||||
|
end
|
||||||
|
column "UTM Source" do |user|
|
||||||
|
user.origin_utm_source
|
||||||
|
end
|
||||||
|
column "UTM Medium" do |user|
|
||||||
|
user.origin_utm_medium
|
||||||
|
end
|
||||||
|
column "UTM Campaign" do |user|
|
||||||
|
user.origin_utm_campaign
|
||||||
|
end
|
||||||
|
column "Referrer" do |user|
|
||||||
|
user.origin_referrer
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -297,7 +297,10 @@ require "jam_ruby/models/affiliate_distribution"
|
||||||
require "jam_ruby/models/teacher_intent"
|
require "jam_ruby/models/teacher_intent"
|
||||||
require "jam_ruby/models/school"
|
require "jam_ruby/models/school"
|
||||||
require "jam_ruby/models/school_invitation"
|
require "jam_ruby/models/school_invitation"
|
||||||
|
require "jam_ruby/models/teacher_instrument"
|
||||||
|
require "jam_ruby/models/teacher_subject"
|
||||||
|
require "jam_ruby/models/teacher_language"
|
||||||
|
require "jam_ruby/models/teacher_genre"
|
||||||
include Jampb
|
include Jampb
|
||||||
|
|
||||||
module JamRuby
|
module JamRuby
|
||||||
|
|
|
||||||
|
|
@ -1770,5 +1770,28 @@ module JamRuby
|
||||||
format.html { render :layout => "from_user_mailer" }
|
format.html { render :layout => "from_user_mailer" }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def lesson_attachment(sender, target, lesson_session, attachment)
|
||||||
|
@sender = sender
|
||||||
|
@target = target
|
||||||
|
@lesson_session = lesson_session
|
||||||
|
@attachment = attachment
|
||||||
|
|
||||||
|
|
||||||
|
email = target.email
|
||||||
|
@subject = "An attachment has been added to your lesson by #{sender.name}"
|
||||||
|
unique_args = {:type => "lesson_attachment"}
|
||||||
|
|
||||||
|
sendgrid_category "Notification"
|
||||||
|
sendgrid_unique_args :type => unique_args[:type]
|
||||||
|
sendgrid_recipients([email])
|
||||||
|
sendgrid_substitute('@USERID', [@target.id])
|
||||||
|
|
||||||
|
mail(:to => email, :subject => @subject) do |format|
|
||||||
|
format.text
|
||||||
|
format.html { render :layout => "from_user_mailer" }
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
<% provide(:title, @subject) %>
|
||||||
|
<% provide(:photo_url, @sender.resolved_photo_url) %>
|
||||||
|
|
||||||
|
<% content_for :note do %>
|
||||||
|
<p>
|
||||||
|
<% if @attachment.is_a?(JamRuby::MusicNotation) %>
|
||||||
|
<% if @attachment.is_notation? %>
|
||||||
|
A music notation has been added to your lesson. You can download <a style="color:#fc0" href="<%= APP_CONFIG.external_root_url + "/api/music_notations/#{@attachment.id}" %>"><%= @attachment.file_name %></a> directly or at any time in the message window for this lesson.
|
||||||
|
<% else %>
|
||||||
|
A audio file has been added to your lesson. You can download <a style="color:#fc0" href="<%= APP_CONFIG.external_root_url + "/api/music_notations/#{@attachment.id}" %>"><%= @attachment.file_name %></a> directly or at any time in the message window for this lesson.
|
||||||
|
<% end %>
|
||||||
|
<% else %>
|
||||||
|
A recording named "<%= @attachment.name %>" has been added to your lesson. It can be viewed <a style="color:#fc0" href="<%= APP_CONFIG.external_root_url + "/recordings/#{@attachment.id}" %>">here</a> or found within the message window for this lesson.
|
||||||
|
<% end %>
|
||||||
|
<p>
|
||||||
|
<a href="<%= @lesson_session.web_url %>" style="margin: 8px 0 0 0;background-color: #ed3618;border: solid 1px #F27861;padding: 3px 10px;font-size: 12px;font-weight: 300;cursor: pointer;color: #FC9;text-decoration: none;line-height: 12px;text-align: center;">VIEW LESSON DETAILS</a>
|
||||||
|
</p>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
<% if @attachment.is_a?(JamRuby::MusicNotation) %>
|
||||||
|
<% if @attachment.is_notation? %>
|
||||||
|
A music notation has been added to your lesson. You can download (<%= APP_CONFIG.external_root_url + "/api/music_notations/#{@attachment.id}" %>">)<%= @attachment.file_name %> directly or at any time in the message window for this lesson.
|
||||||
|
<% else %>
|
||||||
|
A audio file has been added to your lesson. You can download (<%= APP_CONFIG.external_root_url + "/api/music_notations/#{@attachment.id}" %>">)<%= @attachment.file_name %> directly or at any time in the message window for this lesson.
|
||||||
|
<% end %>
|
||||||
|
<% else %>
|
||||||
|
A recording named "<%= @attachment.name %>" has been added to your lesson. It can be viewed (<%= APP_CONFIG.external_root_url + "/recordings/#{@attachment.id}" %>">) here or found within the message window for this lesson.
|
||||||
|
<% end %>
|
||||||
|
VIEW LESSON DETAILS (<%= @lesson_session.web_url %>)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -16,7 +16,8 @@ module JamRuby
|
||||||
has_and_belongs_to_many :recordings, :class_name => "JamRuby::Recording", :join_table => "recordings_genres"
|
has_and_belongs_to_many :recordings, :class_name => "JamRuby::Recording", :join_table => "recordings_genres"
|
||||||
|
|
||||||
# teachers
|
# teachers
|
||||||
has_and_belongs_to_many :teachers, :class_name => "JamRuby::Teacher", :join_table => "teachers_genres"
|
has_many :teachers, :class_name => "JamRuby::Teacher", :through => :teachers_genres
|
||||||
|
has_many :teachers_genres, :class_name => "JamRuby::TeacherGenre"
|
||||||
|
|
||||||
# jam tracks
|
# jam tracks
|
||||||
has_many :genres_jam_tracks, :class_name => "JamRuby::GenreJamTrack", :foreign_key => "genre_id"
|
has_many :genres_jam_tracks, :class_name => "JamRuby::GenreJamTrack", :foreign_key => "genre_id"
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,8 @@ module JamRuby
|
||||||
has_and_belongs_to_many :music_sessions, :class_name => "JamRuby::ActiveMusicSession", :join_table => "genres_music_sessions"
|
has_and_belongs_to_many :music_sessions, :class_name => "JamRuby::ActiveMusicSession", :join_table => "genres_music_sessions"
|
||||||
|
|
||||||
# teachers
|
# teachers
|
||||||
has_and_belongs_to_many :teachers, :class_name => "JamRuby::Teacher", :join_table => "teachers_instruments"
|
has_many :teachers, :class_name => "JamRuby::Teacher", through: :teachers_instruments
|
||||||
|
has_many :teachers_instruments, class_name: "JamRuby::TeacherInstrument"
|
||||||
|
|
||||||
def self.standard_list
|
def self.standard_list
|
||||||
return Instrument.where('instruments.popularity > 0').order('instruments.description ASC')
|
return Instrument.where('instruments.popularity > 0').order('instruments.description ASC')
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@ module JamRuby
|
||||||
class Language < ActiveRecord::Base
|
class Language < ActiveRecord::Base
|
||||||
include HtmlSanitize
|
include HtmlSanitize
|
||||||
html_sanitize strict: [:name, :description]
|
html_sanitize strict: [:name, :description]
|
||||||
has_and_belongs_to_many :teachers, :class_name => "JamRuby::Teacher", :join_table => "teachers_languages"
|
has_many :teachers, :class_name => "JamRuby::Teacher", :through => :teachers_languages
|
||||||
|
has_many :teachers_languages, class_name: "JamRuby::TeacherLanguage"
|
||||||
|
|
||||||
def self.english_sort
|
def self.english_sort
|
||||||
languages = Language.order(:description)
|
languages = Language.order(:description)
|
||||||
|
|
|
||||||
|
|
@ -243,7 +243,12 @@ module JamRuby
|
||||||
minimum_start_time = create_minimum_booking_time
|
minimum_start_time = create_minimum_booking_time
|
||||||
|
|
||||||
# get all sessions that are already scheduled for this booking ahead of the minimum time
|
# get all sessions that are already scheduled for this booking ahead of the minimum time
|
||||||
sessions = MusicSession.joins(:lesson_session).where("lesson_sessions.lesson_booking_id = ?", id).where("scheduled_start is not null").where("scheduled_start > ?", minimum_start_time).order(:created_at)
|
|
||||||
|
sessions= MusicSession.joins(:lesson_session).where("lesson_sessions.lesson_booking_id = ?", id).where("scheduled_start is not null").order(:created_at)
|
||||||
|
if recurring
|
||||||
|
# only want times ahead of this for recurring
|
||||||
|
sessions = sessions.where("scheduled_start > ?", minimum_start_time)
|
||||||
|
end
|
||||||
|
|
||||||
if @default_slot_did_change
|
if @default_slot_did_change
|
||||||
# # adjust all session times
|
# # adjust all session times
|
||||||
|
|
@ -421,6 +426,9 @@ module JamRuby
|
||||||
self.errors.add(:status, "This lesson is already #{self.status}.")
|
self.errors.add(:status, "This lesson is already #{self.status}.")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if self.accepter.nil?
|
||||||
|
self.errors.add(:accepter, "No one has been indicated as accepting the lesson")
|
||||||
|
end
|
||||||
self.accepting = false
|
self.accepting = false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,9 @@ module JamRuby
|
||||||
validate :validate_proposer
|
validate :validate_proposer
|
||||||
before_validation :before_validation
|
before_validation :before_validation
|
||||||
|
|
||||||
|
def is_recurring?
|
||||||
|
slot_type == SLOT_TYPE_RECURRING
|
||||||
|
end
|
||||||
def before_validation
|
def before_validation
|
||||||
if proposer.nil?
|
if proposer.nil?
|
||||||
self.proposer = container.student
|
self.proposer = container.student
|
||||||
|
|
@ -81,6 +84,7 @@ module JamRuby
|
||||||
candidate = scheduled_time(i + week_offset)
|
candidate = scheduled_time(i + week_offset)
|
||||||
|
|
||||||
#puts "#{i}: candidate #{candidate} week_offset:#{week_offset}"
|
#puts "#{i}: candidate #{candidate} week_offset:#{week_offset}"
|
||||||
|
#puts "DAY_OF_WEEK #{day_of_week}"
|
||||||
if day_of_week && candidate <= minimum_start_time
|
if day_of_week && candidate <= minimum_start_time
|
||||||
# move it up a week
|
# move it up a week
|
||||||
week_offset += 1
|
week_offset += 1
|
||||||
|
|
@ -173,7 +177,7 @@ module JamRuby
|
||||||
duration = lesson_length * 60 # convert from minutes to seconds
|
duration = lesson_length * 60 # convert from minutes to seconds
|
||||||
end_time = start_time + duration
|
end_time = start_time + duration
|
||||||
if with_timezone
|
if with_timezone
|
||||||
"#{start_time.strftime("%A, %B %e")}, #{start_time.strftime("%l:%M").strip}-#{end_time.strftime("%l:%M %p").strip} #{tz.pretty_name}"
|
"#{start_time.strftime("%A, %B %e")}, #{start_time.strftime("%l:%M").strip}-#{end_time.strftime("%l:%M %p").strip} (#{tz.pretty_name})"
|
||||||
else
|
else
|
||||||
"#{start_time.strftime("%A, %B %e")} - #{start_time.strftime("%l:%M%P").strip}"
|
"#{start_time.strftime("%A, %B %e")} - #{start_time.strftime("%l:%M%P").strip}"
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ module JamRuby
|
||||||
include HtmlSanitize
|
include HtmlSanitize
|
||||||
html_sanitize strict: [:cancel_message]
|
html_sanitize strict: [:cancel_message]
|
||||||
|
|
||||||
attr_accessor :accepting, :creating, :countering, :autocanceling, :countered_slot, :countered_lesson, :canceling, :assigned_student
|
attr_accessor :accepting, :creating, :countering, :countering_flag, :autocanceling, :countered_slot, :countered_lesson, :canceling, :assigned_student
|
||||||
|
|
||||||
|
|
||||||
@@log = Logging.logger[LessonSession]
|
@@log = Logging.logger[LessonSession]
|
||||||
|
|
@ -61,6 +61,7 @@ module JamRuby
|
||||||
validates :post_processed, inclusion: {in: [true, false]}
|
validates :post_processed, inclusion: {in: [true, false]}
|
||||||
|
|
||||||
validate :validate_creating, :if => :creating
|
validate :validate_creating, :if => :creating
|
||||||
|
validate :validate_countering, :if => :countering_flag
|
||||||
validate :validate_accepted, :if => :accepting
|
validate :validate_accepted, :if => :accepting
|
||||||
validate :validate_canceled, :if => :canceling
|
validate :validate_canceled, :if => :canceling
|
||||||
validate :validate_autocancel, :if => :autocanceling
|
validate :validate_autocancel, :if => :autocanceling
|
||||||
|
|
@ -165,7 +166,7 @@ module JamRuby
|
||||||
end
|
end
|
||||||
|
|
||||||
# test drives don't have a lesson_payment_charge, so we don't join against them
|
# test drives don't have a lesson_payment_charge, so we don't join against them
|
||||||
MusicSession.joins(lesson_session: [:lesson_booking]).where('lesson_sessions.status = ?', LessonSession::STATUS_COMPLETED).where('lesson_sessions.lesson_type = ?', LESSON_TYPE_TEST_DRIVE).where("session_removed_at IS NOT NULL OR ? > scheduled_start + (INTERVAL '1 minutes' * duration)", Time.now).where('analysed = true').where('lesson_sessions.post_processed = false').each do |music_session|
|
MusicSession.joins(lesson_session: [:lesson_booking]).where('lesson_sessions.status = ?', LessonSession::STATUS_COMPLETED).where('lesson_sessions.lesson_type = ?', LESSON_TYPE_TEST_DRIVE).where("? > scheduled_start + (INTERVAL '1 minutes' * duration)", Time.now).where('analysed = true').where('lesson_sessions.post_processed = false').each do |music_session|
|
||||||
lession_session = music_session.lesson_session
|
lession_session = music_session.lesson_session
|
||||||
lession_session.session_completed
|
lession_session.session_completed
|
||||||
end
|
end
|
||||||
|
|
@ -366,7 +367,9 @@ module JamRuby
|
||||||
end
|
end
|
||||||
|
|
||||||
def recurring_completed
|
def recurring_completed
|
||||||
|
puts "RECURRING COMPLETED #{success}"
|
||||||
if success
|
if success
|
||||||
|
|
||||||
if lesson_booking.is_monthly_payment?
|
if lesson_booking.is_monthly_payment?
|
||||||
# monthly payments are handled at beginning of month; just poke with email, and move on
|
# monthly payments are handled at beginning of month; just poke with email, and move on
|
||||||
|
|
||||||
|
|
@ -399,6 +402,7 @@ module JamRuby
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
|
puts "STUDENT NO BILL SENT #{self.id}"
|
||||||
if !sent_notices
|
if !sent_notices
|
||||||
if !school_on_school?
|
if !school_on_school?
|
||||||
# bad session; just poke user
|
# bad session; just poke user
|
||||||
|
|
@ -422,6 +426,7 @@ module JamRuby
|
||||||
else
|
else
|
||||||
if !sent_notices
|
if !sent_notices
|
||||||
if !school_on_school?
|
if !school_on_school?
|
||||||
|
puts "STUDENT NO BILL SENT #{success}"
|
||||||
UserMailer.student_lesson_normal_no_bill(self).deliver
|
UserMailer.student_lesson_normal_no_bill(self).deliver
|
||||||
UserMailer.teacher_lesson_normal_no_bill(self).deliver
|
UserMailer.teacher_lesson_normal_no_bill(self).deliver
|
||||||
end
|
end
|
||||||
|
|
@ -501,6 +506,22 @@ module JamRuby
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def validate_countering
|
||||||
|
|
||||||
|
if counter_slot.nil?
|
||||||
|
errors.add(:counter_slot, "must be specified")
|
||||||
|
elsif !approved_before? && (status == STATUS_REQUESTED || status == STATUS_COUNTERED)
|
||||||
|
if recurring && !counter_slot.update_all
|
||||||
|
errors.add(:counter_slot, "Only 'update all' counter-proposals are allowed for un-approved, recurring lessons")
|
||||||
|
end
|
||||||
|
|
||||||
|
if recurring && !counter_slot.is_recurring?
|
||||||
|
errors.add(:counter_slot, "Only 'recurring' counter-proposals are allowed for un-approved, recurring lessons")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self.countering_flag = false
|
||||||
|
end
|
||||||
def validate_accepted
|
def validate_accepted
|
||||||
if self.status_was != STATUS_REQUESTED && self.status_was != STATUS_COUNTERED
|
if self.status_was != STATUS_REQUESTED && self.status_was != STATUS_COUNTERED
|
||||||
self.errors.add(:status, "This session is already #{self.status_was}.")
|
self.errors.add(:status, "This session is already #{self.status_was}.")
|
||||||
|
|
@ -787,6 +808,7 @@ module JamRuby
|
||||||
|
|
||||||
update_all = slot.update_all || !lesson_booking.recurring
|
update_all = slot.update_all || !lesson_booking.recurring
|
||||||
self.countering = true
|
self.countering = true
|
||||||
|
self.countering_flag = true
|
||||||
slot.proposer = proposer
|
slot.proposer = proposer
|
||||||
slot.lesson_session = self
|
slot.lesson_session = self
|
||||||
slot.message = message
|
slot.message = message
|
||||||
|
|
@ -796,11 +818,12 @@ module JamRuby
|
||||||
self.countered_slot = slot
|
self.countered_slot = slot
|
||||||
self.countered_lesson = self
|
self.countered_lesson = self
|
||||||
self.status = STATUS_COUNTERED
|
self.status = STATUS_COUNTERED
|
||||||
if !update_all
|
#if !update_all
|
||||||
self.counter_slot = slot
|
self.counter_slot = slot
|
||||||
end
|
#end
|
||||||
if self.save
|
if self.save
|
||||||
if update_all && !lesson_booking.counter(self, proposer, slot)
|
#if update_all && !lesson_booking.counter(self, proposer, slot)
|
||||||
|
if !lesson_booking.counter(self, proposer, slot)
|
||||||
response = lesson_booking
|
response = lesson_booking
|
||||||
raise ActiveRecord::Rollback
|
raise ActiveRecord::Rollback
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,9 @@ module JamRuby
|
||||||
s3_manager.sign_url(self[:file_url], {:expires => expiration_time, :secure => true})
|
s3_manager.sign_url(self[:file_url], {:expires => expiration_time, :secure => true})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def is_notation?
|
||||||
|
self.attachment_type == TYPE_NOTATION
|
||||||
|
end
|
||||||
private
|
private
|
||||||
|
|
||||||
def self.construct_filename(notation)
|
def self.construct_filename(notation)
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ module JamRuby
|
||||||
class Subject < ActiveRecord::Base
|
class Subject < ActiveRecord::Base
|
||||||
include HtmlSanitize
|
include HtmlSanitize
|
||||||
html_sanitize strict: [:name, :description]
|
html_sanitize strict: [:name, :description]
|
||||||
has_and_belongs_to_many :teachers, :class_name => "JamRuby::Teacher", :join_table => "teachers_subjects"
|
has_many :teachers, :class_name => "JamRuby::Teacher", :through => :teachers_subjects
|
||||||
|
has_many :teachers_subjects, class_name: "JamRuby::TeacherSubject"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,14 @@ module JamRuby
|
||||||
html_sanitize strict: [:biography, :website]
|
html_sanitize strict: [:biography, :website]
|
||||||
attr_accessor :validate_introduction, :validate_basics, :validate_pricing
|
attr_accessor :validate_introduction, :validate_basics, :validate_pricing
|
||||||
attr_accessible :genres, :teacher_experiences, :experiences_teaching, :experiences_education, :experiences_award
|
attr_accessible :genres, :teacher_experiences, :experiences_teaching, :experiences_education, :experiences_award
|
||||||
has_and_belongs_to_many :genres, :class_name => "JamRuby::Genre", :join_table => "teachers_genres", :order => "description"
|
has_many :genres, :class_name => "JamRuby::Genre", :through => :teachers_genres # , :order => "description"
|
||||||
has_and_belongs_to_many :instruments, :class_name => "JamRuby::Instrument", :join_table => "teachers_instruments", :order => "description"
|
has_many :teachers_genres, :class_name => "JamRuby::TeacherGenre"
|
||||||
has_and_belongs_to_many :subjects, :class_name => "JamRuby::Subject", :join_table => "teachers_subjects", :order => "description"
|
has_many :instruments, :class_name => "JamRuby::Instrument", through: :teachers_instruments # , :order => "description"
|
||||||
has_and_belongs_to_many :languages, :class_name => "JamRuby::Language", :join_table => "teachers_languages", :order => "description"
|
has_many :teachers_instruments, class_name: "JamRuby::TeacherInstrument"
|
||||||
|
has_many :subjects, :class_name => "JamRuby::Subject", :through => :teachers_subjects # , :order => "description"
|
||||||
|
has_many :teachers_subjects, class_name: "JamRuby::TeacherSubject"
|
||||||
|
has_many :languages, :class_name => "JamRuby::Language", :through => :teachers_languages # , :order => "description"
|
||||||
|
has_many :teachers_languages, class_name: "JamRuby::TeacherLanguage"
|
||||||
has_many :teacher_experiences, :class_name => "JamRuby::TeacherExperience"
|
has_many :teacher_experiences, :class_name => "JamRuby::TeacherExperience"
|
||||||
has_many :experiences_teaching, :class_name => "JamRuby::TeacherExperience", conditions: {experience_type: 'teaching'}
|
has_many :experiences_teaching, :class_name => "JamRuby::TeacherExperience", conditions: {experience_type: 'teaching'}
|
||||||
has_many :experiences_education, :class_name => "JamRuby::TeacherExperience", conditions: {experience_type: 'education'}
|
has_many :experiences_education, :class_name => "JamRuby::TeacherExperience", conditions: {experience_type: 'education'}
|
||||||
|
|
@ -36,7 +40,7 @@ module JamRuby
|
||||||
validate :offer_duration, :if => :validate_pricing
|
validate :offer_duration, :if => :validate_pricing
|
||||||
validate :teaches_ages, :if => :validate_basics
|
validate :teaches_ages, :if => :validate_basics
|
||||||
|
|
||||||
default_scope { includes(:genres).order('created_at desc') }
|
#default_scope { includes(:genres).order('created_at desc') }
|
||||||
|
|
||||||
after_save :update_profile_pct
|
after_save :update_profile_pct
|
||||||
|
|
||||||
|
|
@ -53,7 +57,7 @@ module JamRuby
|
||||||
limit ||= 20
|
limit ||= 20
|
||||||
limit = limit.to_i
|
limit = limit.to_i
|
||||||
|
|
||||||
query = User.joins(:teacher)
|
query = User.unscoped.joins(:teacher)
|
||||||
|
|
||||||
# only show teachers with ready for session set to true
|
# only show teachers with ready for session set to true
|
||||||
query = query.where('teachers.ready_for_session_at IS NOT NULL')
|
query = query.where('teachers.ready_for_session_at IS NOT NULL')
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
module JamRuby
|
||||||
|
class TeacherGenre < ActiveRecord::Base
|
||||||
|
self.table_name = "teachers_genres"
|
||||||
|
|
||||||
|
belongs_to :teacher, class_name: "JamRuby::Teacher"
|
||||||
|
belongs_to :genre, class_name: "JamRuby::Genre"
|
||||||
|
|
||||||
|
validates :teacher, presence:true
|
||||||
|
validates :genre, presence: true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
module JamRuby
|
||||||
|
class TeacherInstrument < ActiveRecord::Base
|
||||||
|
self.table_name = "teachers_instruments"
|
||||||
|
|
||||||
|
belongs_to :teacher, class_name: "JamRuby::Teacher"
|
||||||
|
belongs_to :instrument, class_name: "JamRuby::Instrument"
|
||||||
|
|
||||||
|
validates :teacher, presence:true
|
||||||
|
validates :instrument, presence: true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
module JamRuby
|
||||||
|
class TeacherLanguage < ActiveRecord::Base
|
||||||
|
self.table_name = "teachers_languages"
|
||||||
|
|
||||||
|
belongs_to :teacher, class_name: "JamRuby::Teacher"
|
||||||
|
belongs_to :language, class_name: "JamRuby::Language"
|
||||||
|
|
||||||
|
validates :teacher, presence:true
|
||||||
|
validates :language, presence: true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
module JamRuby
|
||||||
|
class TeacherSubject < ActiveRecord::Base
|
||||||
|
self.table_name = "teachers_subjects"
|
||||||
|
|
||||||
|
belongs_to :teacher, class_name: "JamRuby::Teacher"
|
||||||
|
belongs_to :subject, class_name: "JamRuby::Subject"
|
||||||
|
|
||||||
|
validates :teacher, presence:true
|
||||||
|
validates :subject, presence: true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -51,7 +51,7 @@ module JamRuby
|
||||||
has_many :user_authorizations, :class_name => "JamRuby::UserAuthorization"
|
has_many :user_authorizations, :class_name => "JamRuby::UserAuthorization"
|
||||||
|
|
||||||
has_many :reviews, :class_name => "JamRuby::Review"
|
has_many :reviews, :class_name => "JamRuby::Review"
|
||||||
has_one :review_summary, :class_name => "JamRuby::ReviewSummary"
|
has_one :review_summary, :class_name => "JamRuby::ReviewSummary", as: :target
|
||||||
|
|
||||||
# calendars (for scheduling NOT in music_session)
|
# calendars (for scheduling NOT in music_session)
|
||||||
has_many :calendars, :class_name => "JamRuby::Calendar"
|
has_many :calendars, :class_name => "JamRuby::Calendar"
|
||||||
|
|
@ -2090,7 +2090,7 @@ module JamRuby
|
||||||
LessonBooking.unprocessed(self).where(lesson_type: LessonBooking::LESSON_TYPE_PAID).first
|
LessonBooking.unprocessed(self).where(lesson_type: LessonBooking::LESSON_TYPE_PAID).first
|
||||||
end
|
end
|
||||||
|
|
||||||
def most_recent_test_drive_purchase
|
def most_recent_test_drive_purchase
|
||||||
lesson_purchases.where('lesson_package_type_id in (?)', LessonPackageType.test_drive_package_ids).order('created_at desc').first
|
lesson_purchases.where('lesson_package_type_id in (?)', LessonPackageType.test_drive_package_ids).order('created_at desc').first
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1057,5 +1057,13 @@ FactoryGirl.define do
|
||||||
factory :email_blacklist, class: "JamRuby::EmailBlacklist" do
|
factory :email_blacklist, class: "JamRuby::EmailBlacklist" do
|
||||||
sequence(:email) { |n| "person_#{n}@example.com"}
|
sequence(:email) { |n| "person_#{n}@example.com"}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
factory :music_notation, class: "JamRuby::MusicNotation" do
|
||||||
|
attachment_type {JamRuby::MusicNotation::TYPE_NOTATION}
|
||||||
|
association :user, factory: :user
|
||||||
|
file_url 'abc'
|
||||||
|
size 100
|
||||||
|
file_name 'some_file.jpg'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,6 @@ describe "Monthly Recurring Lesson Flow" do
|
||||||
########## Need validate their credit card
|
########## Need validate their credit card
|
||||||
token = create_stripe_token
|
token = create_stripe_token
|
||||||
result = user.payment_update({token: token, zip: '78759', normal: true, booking_id: booking.id})
|
result = user.payment_update({token: token, zip: '78759', normal: true, booking_id: booking.id})
|
||||||
puts "result #{result.inspect}"
|
|
||||||
booking.reload
|
booking.reload
|
||||||
booking.card_presumed_ok.should be_true
|
booking.card_presumed_ok.should be_true
|
||||||
booking.errors.any?.should be_false
|
booking.errors.any?.should be_false
|
||||||
|
|
@ -118,7 +117,7 @@ describe "Monthly Recurring Lesson Flow" do
|
||||||
|
|
||||||
######## Teacher accepts slot
|
######## Teacher accepts slot
|
||||||
UserMailer.deliveries.clear
|
UserMailer.deliveries.clear
|
||||||
lesson_session.accept({message: 'Yeah I got this', slot: student_counter.id, update_all: false})
|
lesson_session.accept({message: 'Yeah I got this', slot: student_counter.id, update_all: false, accepter: teacher_user})
|
||||||
UserMailer.deliveries.each do |del|
|
UserMailer.deliveries.each do |del|
|
||||||
# puts del.inspect
|
# puts del.inspect
|
||||||
end
|
end
|
||||||
|
|
@ -203,7 +202,7 @@ describe "Monthly Recurring Lesson Flow" do
|
||||||
payment = TeacherPayment.first
|
payment = TeacherPayment.first
|
||||||
payment.amount_in_cents.should eql 3000
|
payment.amount_in_cents.should eql 3000
|
||||||
payment.fee_in_cents.should eql (3000 * 0.28).round
|
payment.fee_in_cents.should eql (3000 * 0.28).round
|
||||||
payment.teacher_payment_charge.amount_in_cents.should eql 3000
|
payment.teacher_payment_charge.amount_in_cents.should eql (3000 + 3000 * APP_CONFIG.stripe[:ach_pct]).round
|
||||||
payment.teacher_payment_charge.fee_in_cents.should eql (3000 * 0.28).round
|
payment.teacher_payment_charge.fee_in_cents.should eql (3000 * 0.28).round
|
||||||
payment.teacher.should eql teacher_user
|
payment.teacher.should eql teacher_user
|
||||||
payment.teacher_distribution.should eql teacher_distribution
|
payment.teacher_distribution.should eql teacher_distribution
|
||||||
|
|
@ -320,7 +319,7 @@ describe "Monthly Recurring Lesson Flow" do
|
||||||
|
|
||||||
######## Teacher accepts slot
|
######## Teacher accepts slot
|
||||||
UserMailer.deliveries.clear
|
UserMailer.deliveries.clear
|
||||||
lesson_session.accept({message: 'Yeah I got this', slot: student_counter.id, update_all: false})
|
lesson_session.accept({message: 'Yeah I got this', slot: student_counter.id, update_all: false, accepter: teacher_user})
|
||||||
UserMailer.deliveries.each do |del|
|
UserMailer.deliveries.each do |del|
|
||||||
# puts del.inspect
|
# puts del.inspect
|
||||||
end
|
end
|
||||||
|
|
@ -419,6 +418,7 @@ describe "Monthly Recurring Lesson Flow" do
|
||||||
|
|
||||||
|
|
||||||
it "affiliate gets their cut" do
|
it "affiliate gets their cut" do
|
||||||
|
Timecop.travel(2016, 05, 15)
|
||||||
user.affiliate_referral = affiliate_partner
|
user.affiliate_referral = affiliate_partner
|
||||||
user.save!
|
user.save!
|
||||||
teacher_user.affiliate_referral = affiliate_partner2
|
teacher_user.affiliate_referral = affiliate_partner2
|
||||||
|
|
@ -429,7 +429,6 @@ describe "Monthly Recurring Lesson Flow" do
|
||||||
|
|
||||||
user.reload
|
user.reload
|
||||||
|
|
||||||
puts "user.lesson_purchases #{user.lesson_purchases}"
|
|
||||||
user.lesson_purchases.count.should eql 1
|
user.lesson_purchases.count.should eql 1
|
||||||
lesson_package_purchase = user.lesson_purchases.first
|
lesson_package_purchase = user.lesson_purchases.first
|
||||||
teacher_distribution = lesson_package_purchase.teacher_distribution
|
teacher_distribution = lesson_package_purchase.teacher_distribution
|
||||||
|
|
@ -447,6 +446,7 @@ describe "Monthly Recurring Lesson Flow" do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "school affiliate gets nothing when teacher school is involved" do
|
it "school affiliate gets nothing when teacher school is involved" do
|
||||||
|
Timecop.travel(2016, 05, 15)
|
||||||
teacher.school = school
|
teacher.school = school
|
||||||
teacher.save!
|
teacher.save!
|
||||||
|
|
||||||
|
|
@ -469,6 +469,8 @@ describe "Monthly Recurring Lesson Flow" do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "student school affiliates gets cut when student school is involved. so does teacher's" do
|
it "student school affiliates gets cut when student school is involved. so does teacher's" do
|
||||||
|
# in the middle of the month so that we don't get the next month's in-advance purchase put on us
|
||||||
|
Timecop.travel(2016, 05, 15)
|
||||||
user.affiliate_referral = school.affiliate_partner
|
user.affiliate_referral = school.affiliate_partner
|
||||||
user.save!
|
user.save!
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ describe "Normal Lesson Flow" do
|
||||||
######## Teacher accepts slot
|
######## Teacher accepts slot
|
||||||
UserMailer.deliveries.clear
|
UserMailer.deliveries.clear
|
||||||
|
|
||||||
lesson_session.accept({message: 'Yeah I got this', slot: booking.default_slot.id, update_all: false})
|
lesson_session.accept({message: 'Yeah I got this', slot: booking.default_slot.id, update_all: false, accepter: teacher_user})
|
||||||
lesson_session.errors.any?.should be_false
|
lesson_session.errors.any?.should be_false
|
||||||
lesson_session.reload
|
lesson_session.reload
|
||||||
lesson_session.slot.should eql booking.default_slot
|
lesson_session.slot.should eql booking.default_slot
|
||||||
|
|
@ -251,7 +251,7 @@ describe "Normal Lesson Flow" do
|
||||||
payment = TeacherPayment.first
|
payment = TeacherPayment.first
|
||||||
payment.amount_in_cents.should eql 3000
|
payment.amount_in_cents.should eql 3000
|
||||||
payment.fee_in_cents.should eql (3000 * 0.28).round
|
payment.fee_in_cents.should eql (3000 * 0.28).round
|
||||||
payment.teacher_payment_charge.amount_in_cents.should eql 3000
|
payment.teacher_payment_charge.amount_in_cents.should eql (3000 + 3000 * APP_CONFIG.stripe[:ach_pct]).round
|
||||||
payment.teacher_payment_charge.fee_in_cents.should eql (3000 * 0.28).round
|
payment.teacher_payment_charge.fee_in_cents.should eql (3000 * 0.28).round
|
||||||
payment.teacher.should eql teacher_user
|
payment.teacher.should eql teacher_user
|
||||||
payment.teacher_distribution.should eql teacher_distribution
|
payment.teacher_distribution.should eql teacher_distribution
|
||||||
|
|
@ -352,7 +352,7 @@ describe "Normal Lesson Flow" do
|
||||||
######## Teacher accepts slot
|
######## Teacher accepts slot
|
||||||
UserMailer.deliveries.clear
|
UserMailer.deliveries.clear
|
||||||
|
|
||||||
lesson_session.accept({message: 'Yeah I got this', slot: student_counter.id, update_all: false})
|
lesson_session.accept({message: 'Yeah I got this', slot: student_counter.id, update_all: false,accepter: teacher_user})
|
||||||
lesson_session.errors.any?.should be_false
|
lesson_session.errors.any?.should be_false
|
||||||
lesson_session.reload
|
lesson_session.reload
|
||||||
lesson_session.slot.should eql student_counter
|
lesson_session.slot.should eql student_counter
|
||||||
|
|
@ -508,7 +508,7 @@ describe "Normal Lesson Flow" do
|
||||||
######## Teacher accepts slot
|
######## Teacher accepts slot
|
||||||
UserMailer.deliveries.clear
|
UserMailer.deliveries.clear
|
||||||
|
|
||||||
lesson_session.accept({message: 'Yeah I got this', slot: student_counter.id, update_all: false})
|
lesson_session.accept({message: 'Yeah I got this', slot: student_counter.id, update_all: false, accepter: teacher_user})
|
||||||
lesson_session.errors.any?.should be_false
|
lesson_session.errors.any?.should be_false
|
||||||
lesson_session.reload
|
lesson_session.reload
|
||||||
lesson_session.slot.should eql student_counter
|
lesson_session.slot.should eql student_counter
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ describe "Recurring Lesson Flow" do
|
||||||
booking.status.should eql LessonBooking::STATUS_REQUESTED
|
booking.status.should eql LessonBooking::STATUS_REQUESTED
|
||||||
|
|
||||||
######### Teacher counters with new slot
|
######### Teacher counters with new slot
|
||||||
teacher_countered_slot = FactoryGirl.build(:lesson_booking_slot_single, hour: 14, update_all: true)
|
teacher_countered_slot = FactoryGirl.build(:lesson_booking_slot_recurring, hour: 14, update_all: true)
|
||||||
UserMailer.deliveries.clear
|
UserMailer.deliveries.clear
|
||||||
lesson_session.counter({proposer: teacher_user, slot: teacher_countered_slot, message: 'Does this work?'})
|
lesson_session.counter({proposer: teacher_user, slot: teacher_countered_slot, message: 'Does this work?'})
|
||||||
booking.reload
|
booking.reload
|
||||||
|
|
@ -81,7 +81,7 @@ describe "Recurring Lesson Flow" do
|
||||||
#notification.message.should eql "Instructor has proposed a different time for your lesson."
|
#notification.message.should eql "Instructor has proposed a different time for your lesson."
|
||||||
|
|
||||||
######### Student counters with new slot
|
######### Student counters with new slot
|
||||||
student_countered_slot = FactoryGirl.build(:lesson_booking_slot_single, hour: 16, update_all: true)
|
student_countered_slot = FactoryGirl.build(:lesson_booking_slot_recurring, hour: 16, update_all: true)
|
||||||
UserMailer.deliveries.clear
|
UserMailer.deliveries.clear
|
||||||
lesson_session.counter({proposer: user, slot: student_countered_slot, message: 'Does this work better?'})
|
lesson_session.counter({proposer: user, slot: student_countered_slot, message: 'Does this work better?'})
|
||||||
lesson_session.errors.any?.should be false
|
lesson_session.errors.any?.should be false
|
||||||
|
|
@ -105,7 +105,7 @@ describe "Recurring Lesson Flow" do
|
||||||
|
|
||||||
######## Teacher accepts slot
|
######## Teacher accepts slot
|
||||||
UserMailer.deliveries.clear
|
UserMailer.deliveries.clear
|
||||||
lesson_session.accept({message: 'Yeah I got this', slot: student_counter.id, update_all: false})
|
lesson_session.accept({message: 'Yeah I got this', slot: student_counter.id, accepter: teacher_user})
|
||||||
UserMailer.deliveries.each do |del|
|
UserMailer.deliveries.each do |del|
|
||||||
# puts del.inspect
|
# puts del.inspect
|
||||||
end
|
end
|
||||||
|
|
@ -132,7 +132,11 @@ describe "Recurring Lesson Flow" do
|
||||||
notification.student_directed.should eql true
|
notification.student_directed.should eql true
|
||||||
notification.purpose.should eql 'accept'
|
notification.purpose.should eql 'accept'
|
||||||
notification.description.should eql NotificationTypes::LESSON_MESSAGE
|
notification.description.should eql NotificationTypes::LESSON_MESSAGE
|
||||||
|
user.reload
|
||||||
|
user.sales.length.should eql 0
|
||||||
|
|
||||||
|
booking.reload
|
||||||
|
booking.lesson_sessions[0].scheduled_start.should_not eql booking.lesson_sessions[1].scheduled_start
|
||||||
|
|
||||||
# teacher & student get into session
|
# teacher & student get into session
|
||||||
start = lesson_session.scheduled_start
|
start = lesson_session.scheduled_start
|
||||||
|
|
@ -181,6 +185,9 @@ describe "Recurring Lesson Flow" do
|
||||||
lesson_session.sent_billing_notices.should be true
|
lesson_session.sent_billing_notices.should be true
|
||||||
user.reload
|
user.reload
|
||||||
user.remaining_test_drives.should eql 0
|
user.remaining_test_drives.should eql 0
|
||||||
|
UserMailer.deliveries.each do |d|
|
||||||
|
puts d.subject
|
||||||
|
end
|
||||||
UserMailer.deliveries.length.should eql 2 # one for student, one for teacher
|
UserMailer.deliveries.length.should eql 2 # one for student, one for teacher
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -140,7 +140,7 @@ describe "TestDrive Lesson Flow" do
|
||||||
######## Teacher accepts slot
|
######## Teacher accepts slot
|
||||||
UserMailer.deliveries.clear
|
UserMailer.deliveries.clear
|
||||||
|
|
||||||
lesson_session.accept({message: 'Yeah I got this', slot: student_counter.id, update_all: false})
|
lesson_session.accept({message: 'Yeah I got this', slot: student_counter.id, update_all: false, accepter: teacher_user})
|
||||||
lesson_session.errors.any?.should be_false
|
lesson_session.errors.any?.should be_false
|
||||||
lesson_session.reload
|
lesson_session.reload
|
||||||
lesson_session.slot.should eql student_counter
|
lesson_session.slot.should eql student_counter
|
||||||
|
|
@ -237,7 +237,7 @@ describe "TestDrive Lesson Flow" do
|
||||||
teacher_distribution.ready.should be_true
|
teacher_distribution.ready.should be_true
|
||||||
teacher_distribution.distributed.should be_true
|
teacher_distribution.distributed.should be_true
|
||||||
|
|
||||||
teacher_payment.teacher_payment_charge.amount_in_cents.should eql 1000
|
teacher_payment.teacher_payment_charge.amount_in_cents.should eql (1000 + 1000 * APP_CONFIG.stripe[:ach_pct]).round
|
||||||
teacher_payment.teacher_payment_charge.fee_in_cents.should eql 0
|
teacher_payment.teacher_payment_charge.fee_in_cents.should eql 0
|
||||||
|
|
||||||
user.sales.count.should eql 1
|
user.sales.count.should eql 1
|
||||||
|
|
|
||||||
|
|
@ -741,6 +741,14 @@ describe LessonBooking do
|
||||||
|
|
||||||
UserMailer.deliveries.clear
|
UserMailer.deliveries.clear
|
||||||
Timecop.freeze(7.days.ago)
|
Timecop.freeze(7.days.ago)
|
||||||
|
|
||||||
|
mailer = mock
|
||||||
|
mailer.should_receive(:deliver).exactly(2).times
|
||||||
|
UserMailer.should_receive(:student_lesson_booking_canceled).and_return(mailer)
|
||||||
|
UserMailer.should_receive(:teacher_lesson_booking_canceled).and_return(mailer)
|
||||||
|
UserMailer.should_receive(:student_lesson_canceled).exactly(0).times
|
||||||
|
UserMailer.should_receive(:teacher_lesson_canceled).exactly(0).times
|
||||||
|
|
||||||
lesson_session.cancel({canceler: user, message: 'meh', slot: booking.default_slot.id, update_all: true})
|
lesson_session.cancel({canceler: user, message: 'meh', slot: booking.default_slot.id, update_all: true})
|
||||||
lesson_session.errors.any?.should be_false
|
lesson_session.errors.any?.should be_false
|
||||||
lesson_session.reload
|
lesson_session.reload
|
||||||
|
|
@ -748,14 +756,32 @@ describe LessonBooking do
|
||||||
booking.reload
|
booking.reload
|
||||||
booking.status.should eql LessonSession::STATUS_CANCELED
|
booking.status.should eql LessonSession::STATUS_CANCELED
|
||||||
booking.canceler.should eql user
|
booking.canceler.should eql user
|
||||||
UserMailer.deliveries.length.should eql 2
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
describe "rescheduling" do
|
describe "rescheduling" do
|
||||||
|
|
||||||
after do
|
it "initial slot is in the past" do
|
||||||
Timecop.return
|
booking = LessonBooking.book_normal(user, teacher_user, valid_single_slots, "Hey I've heard of you before.", false, LessonBooking::PAYMENT_STYLE_SINGLE, 60)
|
||||||
|
|
||||||
|
lesson_session = booking.lesson_sessions[0]
|
||||||
|
|
||||||
|
initial_scheduled_time = lesson_session.scheduled_start
|
||||||
|
|
||||||
|
counter = FactoryGirl.build(:lesson_booking_slot_single, preferred_day: Date.today + 20)
|
||||||
|
lesson_session.counter({proposer: user, slot: counter, message: 'ACtually, let\'s do this instead for just this one'})
|
||||||
|
|
||||||
|
Timecop.travel(initial_scheduled_time + 1)
|
||||||
|
|
||||||
|
lesson_session.accept({accepter: teacher_user, message: 'Yeah I got this', slot: counter, update_all: false})
|
||||||
|
booking.reload
|
||||||
|
booking.status.should eql LessonBooking::STATUS_APPROVED
|
||||||
|
booking.lesson_sessions.count.should eql 1
|
||||||
|
lesson_session.errors.any?.should be_false
|
||||||
|
lesson_session.reload
|
||||||
|
lesson_session.status.should eql LessonSession::STATUS_APPROVED
|
||||||
|
lesson_session.scheduled_start.should eql counter.scheduled_time(0)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "non-recurring, accepted with new slot" do
|
it "non-recurring, accepted with new slot" do
|
||||||
booking = LessonBooking.book_normal(user, teacher_user, valid_single_slots, "Hey I've heard of you before.", false, LessonBooking::PAYMENT_STYLE_SINGLE, 60)
|
booking = LessonBooking.book_normal(user, teacher_user, valid_single_slots, "Hey I've heard of you before.", false, LessonBooking::PAYMENT_STYLE_SINGLE, 60)
|
||||||
lesson_session = booking.lesson_sessions[0]
|
lesson_session = booking.lesson_sessions[0]
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,63 @@ describe LessonSession do
|
||||||
let(:lesson_booking) {b = LessonBooking.book_normal(user, teacher, [slot1, slot2], "Hey I've heard of you before.", false, LessonBooking::PAYMENT_STYLE_SINGLE, 60); b.card_presumed_ok = true; b.save!; b}
|
let(:lesson_booking) {b = LessonBooking.book_normal(user, teacher, [slot1, slot2], "Hey I've heard of you before.", false, LessonBooking::PAYMENT_STYLE_SINGLE, 60); b.card_presumed_ok = true; b.save!; b}
|
||||||
let(:lesson_session) {lesson_booking.lesson_sessions[0]}
|
let(:lesson_session) {lesson_booking.lesson_sessions[0]}
|
||||||
|
|
||||||
|
describe "counter" do
|
||||||
|
describe "recurring" do
|
||||||
|
it "counter madness" do
|
||||||
|
lesson = monthly_lesson(user, teacher, {accept:false})
|
||||||
|
# start with the student
|
||||||
|
invalid = FactoryGirl.build(:lesson_booking_slot_single, update_all: false)
|
||||||
|
|
||||||
|
lesson.counter({proposer: user, message: "crumble and bumble", slot: invalid})
|
||||||
|
lesson.errors.any?.should be_true
|
||||||
|
lesson.errors[:counter_slot].should eql ["Only 'update all' counter-proposals are allowed for un-approved, recurring lessons", "Only 'recurring' counter-proposals are allowed for un-approved, recurring lessons"]
|
||||||
|
lesson.reload
|
||||||
|
lesson.counter_slot.should be_nil
|
||||||
|
|
||||||
|
counter1 = FactoryGirl.build(:lesson_booking_slot_recurring, update_all: true)
|
||||||
|
|
||||||
|
lesson.counter({proposer: user, message: "crumble and bumble take 2", slot: counter1})
|
||||||
|
lesson.errors.any?.should be_false
|
||||||
|
lesson.reload
|
||||||
|
lesson.status.should eql LessonSession::STATUS_COUNTERED
|
||||||
|
lesson.counter_slot.id.should eql counter1.id
|
||||||
|
lesson.lesson_booking.counter_slot.id.should eql counter1.id
|
||||||
|
|
||||||
|
counter2 = FactoryGirl.build(:lesson_booking_slot_recurring, update_all: true)
|
||||||
|
|
||||||
|
lesson.counter({proposer: teacher, message: "crumble and bumble take 3", slot: counter2})
|
||||||
|
lesson.errors.any?.should be_false
|
||||||
|
lesson.reload
|
||||||
|
lesson.status.should eql LessonSession::STATUS_COUNTERED
|
||||||
|
lesson.counter_slot.id.should eql counter2.id
|
||||||
|
lesson.lesson_booking.counter_slot.id.should eql counter2.id
|
||||||
|
|
||||||
|
lesson.accept({accepter: user, message: "burp", slot: counter2})
|
||||||
|
lesson.errors.any?.should be_false
|
||||||
|
lesson.reload
|
||||||
|
lesson.status.should eql LessonSession::STATUS_APPROVED
|
||||||
|
|
||||||
|
counter3 = FactoryGirl.build(:lesson_booking_slot_recurring, update_all: false)
|
||||||
|
|
||||||
|
lesson.counter({proposer: user, message: "crumble and bumble take 4", slot: counter3})
|
||||||
|
lesson.errors.any?.should be_false
|
||||||
|
lesson.reload
|
||||||
|
lesson.status.should eql LessonSession::STATUS_COUNTERED
|
||||||
|
lesson.counter_slot.id.should eql counter3.id
|
||||||
|
lesson.lesson_booking.counter_slot.id.should eql counter3.id
|
||||||
|
|
||||||
|
counter4 = FactoryGirl.build(:lesson_booking_slot_recurring, update_all: true)
|
||||||
|
|
||||||
|
lesson.counter({proposer: teacher, message: "crumble and bumble take 5", slot: counter4})
|
||||||
|
lesson.errors.any?.should be_false
|
||||||
|
lesson.reload
|
||||||
|
lesson.status.should eql LessonSession::STATUS_COUNTERED
|
||||||
|
lesson.counter_slot.id.should eql counter4.id
|
||||||
|
lesson.lesson_booking.counter_slot.id.should eql counter4.id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "autocancel" do
|
describe "autocancel" do
|
||||||
it "can't autocancel in the past" do
|
it "can't autocancel in the past" do
|
||||||
lesson_session.status.should eql LessonSession::STATUS_REQUESTED
|
lesson_session.status.should eql LessonSession::STATUS_REQUESTED
|
||||||
|
|
|
||||||
|
|
@ -45,18 +45,20 @@ describe Teacher do
|
||||||
|
|
||||||
it "instruments" do
|
it "instruments" do
|
||||||
teacher = FactoryGirl.create(:teacher, ready_for_session_at: Time.now)
|
teacher = FactoryGirl.create(:teacher, ready_for_session_at: Time.now)
|
||||||
teachers = Teacher.index(nil, {instruments: ['acoustic guitar']})[:query]
|
#teachers = Teacher.index(nil, {instruments: ['acoustic guitar']})[:query]
|
||||||
teachers.length.should eq 0
|
#teachers.length.should eq 0
|
||||||
|
|
||||||
teacher.instruments << Instrument.find('acoustic guitar')
|
teacher.instruments << Instrument.find('acoustic guitar')
|
||||||
teacher.save!
|
teacher.save!
|
||||||
teachers = Teacher.index(nil, {instruments: ['acoustic guitar']})[:query]
|
#teachers = Teacher.index(nil, {instruments: ['acoustic guitar']})[:query]
|
||||||
teachers.length.should eq 1
|
#teachers.length.should eq 1
|
||||||
teachers[0].should eq(teacher.user)
|
#teachers[0].should eq(teacher.user)
|
||||||
|
|
||||||
teacher.instruments << Instrument.find('electric guitar')
|
#teacher.instruments << Instrument.find('electric guitar')
|
||||||
teacher.save!
|
#teacher.save!
|
||||||
|
puts "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
|
||||||
teachers = Teacher.index(nil, {instruments: ['acoustic guitar']})[:query]
|
teachers = Teacher.index(nil, {instruments: ['acoustic guitar']})[:query]
|
||||||
|
puts "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!---"
|
||||||
teachers.length.should eq 1
|
teachers.length.should eq 1
|
||||||
teachers[0].should eq(teacher.user)
|
teachers[0].should eq(teacher.user)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ describe GoogleAnalyticsEvent do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "track band analytics" do
|
describe "track band analytics" do
|
||||||
|
pending "job is commented out"
|
||||||
it 'reports first recording' do
|
it 'reports first recording' do
|
||||||
ResqueSpec.reset!
|
ResqueSpec.reset!
|
||||||
user = FactoryGirl.create(:user)
|
user = FactoryGirl.create(:user)
|
||||||
|
|
@ -26,6 +27,7 @@ describe GoogleAnalyticsEvent do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'reports first real session' do
|
it 'reports first real session' do
|
||||||
|
pending "job is commented out"
|
||||||
ResqueSpec.reset!
|
ResqueSpec.reset!
|
||||||
JamRuby::GoogleAnalyticsEvent::BandSessionTracker.should have_schedule_size_of(0)
|
JamRuby::GoogleAnalyticsEvent::BandSessionTracker.should have_schedule_size_of(0)
|
||||||
user = FactoryGirl.create(:user)
|
user = FactoryGirl.create(:user)
|
||||||
|
|
@ -72,6 +74,7 @@ describe GoogleAnalyticsEvent do
|
||||||
ResqueSpec.reset!
|
ResqueSpec.reset!
|
||||||
end
|
end
|
||||||
it 'reports size increment' do
|
it 'reports size increment' do
|
||||||
|
pending "job is commented out"
|
||||||
user = FactoryGirl.create(:user)
|
user = FactoryGirl.create(:user)
|
||||||
music_session = FactoryGirl.create(:active_music_session,
|
music_session = FactoryGirl.create(:active_music_session,
|
||||||
:creator => user,
|
:creator => user,
|
||||||
|
|
@ -86,6 +89,7 @@ describe GoogleAnalyticsEvent do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'reports duration' do
|
it 'reports duration' do
|
||||||
|
pending "job is commented out"
|
||||||
user = FactoryGirl.create(:user)
|
user = FactoryGirl.create(:user)
|
||||||
JamRuby::GoogleAnalyticsEvent::SessionDurationTracker.should have_schedule_size_of(0)
|
JamRuby::GoogleAnalyticsEvent::SessionDurationTracker.should have_schedule_size_of(0)
|
||||||
music_session = FactoryGirl.create(:active_music_session,
|
music_session = FactoryGirl.create(:active_music_session,
|
||||||
|
|
|
||||||
|
|
@ -162,6 +162,26 @@ describe "RenderMailers", :slow => true do
|
||||||
UserMailer.deliveries.clear
|
UserMailer.deliveries.clear
|
||||||
UserMailer.lesson_starting_soon_student(lesson).deliver
|
UserMailer.lesson_starting_soon_student(lesson).deliver
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "music_notation_attachment" do
|
||||||
|
@filename = "music_notation_attachment"
|
||||||
|
|
||||||
|
lesson = testdrive_lesson(user, teacher)
|
||||||
|
|
||||||
|
UserMailer.deliveries.clear
|
||||||
|
notation = FactoryGirl.create(:music_notation, user: user)
|
||||||
|
UserMailer.lesson_attachment(user, teacher, lesson, notation).deliver
|
||||||
|
end
|
||||||
|
|
||||||
|
it "recording_attachment" do
|
||||||
|
@filename = "recording_attachment"
|
||||||
|
|
||||||
|
lesson = testdrive_lesson(user, teacher)
|
||||||
|
|
||||||
|
UserMailer.deliveries.clear
|
||||||
|
claim = FactoryGirl.create(:claimed_recording, user: user)
|
||||||
|
UserMailer.lesson_attachment(user, teacher, lesson, claim).deliver
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ require 'timecop'
|
||||||
require 'resque_spec/scheduler'
|
require 'resque_spec/scheduler'
|
||||||
|
|
||||||
# uncomment this to see active record logs
|
# uncomment this to see active record logs
|
||||||
# ActiveRecord::Base.logger = Logger.new(STDOUT) if defined?(ActiveRecord::Base)
|
ActiveRecord::Base.logger = Logger.new(STDOUT) if defined?(ActiveRecord::Base)
|
||||||
|
|
||||||
include JamRuby
|
include JamRuby
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,6 @@ def book_lesson(user, teacher, options)
|
||||||
LessonPackagePurchase.create(user, booking, LessonPackageType.package_for_test_drive_count(options[:package_count]))
|
LessonPackagePurchase.create(user, booking, LessonPackageType.package_for_test_drive_count(options[:package_count]))
|
||||||
end
|
end
|
||||||
elsif options[:monthly]
|
elsif options[:monthly]
|
||||||
puts "did it"
|
|
||||||
LessonPackagePurchase.create(user, booking, LessonPackageType.single, Date.today.year, Date.today.month)
|
LessonPackagePurchase.create(user, booking, LessonPackageType.single, Date.today.year, Date.today.month)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -83,7 +82,7 @@ def book_lesson(user, teacher, options)
|
||||||
end
|
end
|
||||||
|
|
||||||
if options[:accept]
|
if options[:accept]
|
||||||
lesson.accept({message: 'Yeah I got this', slot: slots[0]})
|
lesson.accept({message: 'Yeah I got this', slot: slots[0], accepter: teacher})
|
||||||
lesson.errors.any?.should be_false
|
lesson.errors.any?.should be_false
|
||||||
lesson.reload
|
lesson.reload
|
||||||
lesson.slot.should eql slots[0]
|
lesson.slot.should eql slots[0]
|
||||||
|
|
|
||||||
|
|
@ -581,6 +581,9 @@
|
||||||
if(!defaults.show_checkbox) {
|
if(!defaults.show_checkbox) {
|
||||||
$feedItem.find('.select-box').hide();
|
$feedItem.find('.select-box').hide();
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
context.JK.checkbox($feedItem.find('.select-box'))
|
||||||
|
}
|
||||||
if(defaults.hide_avatar) {
|
if(defaults.hide_avatar) {
|
||||||
$feedItem.find('.avatar-small.ib').hide();
|
$feedItem.find('.avatar-small.ib').hide();
|
||||||
}
|
}
|
||||||
|
|
@ -598,6 +601,7 @@
|
||||||
$feedItem.data('original-max-height', $feedItem.css('height'));
|
$feedItem.data('original-max-height', $feedItem.css('height'));
|
||||||
context.JK.bindHoverEvents($feedItem);
|
context.JK.bindHoverEvents($feedItem);
|
||||||
context.JK.bindProfileClickEvents($feedItem);
|
context.JK.bindProfileClickEvents($feedItem);
|
||||||
|
context.JK.popExternalLinks($feedItem)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
logger.warn("skipping feed type: " + feed.type);
|
logger.warn("skipping feed type: " + feed.type);
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,10 @@
|
||||||
ioTargetFail : 'ioTargetFail'
|
ioTargetFail : 'ioTargetFail'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var jamClassReasons = {
|
||||||
|
testDrive: 'TestDrive'
|
||||||
|
}
|
||||||
|
|
||||||
var networkTestFailReasons = {
|
var networkTestFailReasons = {
|
||||||
stun : 'STUN',
|
stun : 'STUN',
|
||||||
bandwidth : 'Bandwidth',
|
bandwidth : 'Bandwidth',
|
||||||
|
|
@ -129,7 +133,8 @@
|
||||||
jkFollow : 'jkFollow',
|
jkFollow : 'jkFollow',
|
||||||
jkFavorite : 'jkFavorite',
|
jkFavorite : 'jkFavorite',
|
||||||
jkComment : 'jkComment',
|
jkComment : 'jkComment',
|
||||||
fileDownload: "DownloadFile"
|
fileDownload: "DownloadFile",
|
||||||
|
jamclass: 'JamClass'
|
||||||
};
|
};
|
||||||
|
|
||||||
// JamTrack categories and actions:
|
// JamTrack categories and actions:
|
||||||
|
|
@ -204,6 +209,11 @@
|
||||||
context.ga('send', 'event', categories.register, action, registrationType);
|
context.ga('send', 'event', categories.register, action, registrationType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function trackTestDrivePurchase(count) {
|
||||||
|
|
||||||
|
context.ga('send', 'event', categories.jamclass, jamClassReasons.testDrive, count);
|
||||||
|
|
||||||
|
}
|
||||||
function trackDownload(platform) {
|
function trackDownload(platform) {
|
||||||
var normalizedPlatform = translatePlatformForGA(platform);
|
var normalizedPlatform = translatePlatformForGA(platform);
|
||||||
|
|
||||||
|
|
@ -490,6 +500,7 @@
|
||||||
GA.virtualPageView = virtualPageView;
|
GA.virtualPageView = virtualPageView;
|
||||||
GA.trackTiming = trackTiming;
|
GA.trackTiming = trackTiming;
|
||||||
GA.trackFileDownload = trackFileDownload;
|
GA.trackFileDownload = trackFileDownload;
|
||||||
|
GA.trackTestDrivePurchase = trackTestDrivePurchase;
|
||||||
|
|
||||||
context.JK.GA = GA;
|
context.JK.GA = GA;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -228,7 +228,7 @@
|
||||||
"Electric Guitar": { "client_id": 50, "server_id": "electric guitar" },
|
"Electric Guitar": { "client_id": 50, "server_id": "electric guitar" },
|
||||||
"Keyboard": { "client_id": 60, "server_id": "keyboard" },
|
"Keyboard": { "client_id": 60, "server_id": "keyboard" },
|
||||||
"Piano": { "client_id": 61, "server_id": "piano" },
|
"Piano": { "client_id": 61, "server_id": "piano" },
|
||||||
"Upright Bass": { "client_id": 62, "server_id": "upright bass" },
|
"Upright Bass": { "client_id": 62, "server_id": "double bass" },
|
||||||
"Voice": { "client_id": 70, "server_id": "voice" },
|
"Voice": { "client_id": 70, "server_id": "voice" },
|
||||||
"Flute": { "client_id": 80, "server_id": "flute" },
|
"Flute": { "client_id": 80, "server_id": "flute" },
|
||||||
"Clarinet": { "client_id": 90, "server_id": "clarinet" },
|
"Clarinet": { "client_id": 90, "server_id": "clarinet" },
|
||||||
|
|
@ -250,6 +250,9 @@
|
||||||
"Other": { "client_id": 250, "server_id": "other" }
|
"Other": { "client_id": 250, "server_id": "other" }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
context.JK.client_to_server_instrument_map = {
|
context.JK.client_to_server_instrument_map = {
|
||||||
10: { "server_id": "acoustic guitar" },
|
10: { "server_id": "acoustic guitar" },
|
||||||
20: { "server_id": "bass guitar" },
|
20: { "server_id": "bass guitar" },
|
||||||
|
|
@ -259,7 +262,7 @@
|
||||||
50: { "server_id": "electric guitar" },
|
50: { "server_id": "electric guitar" },
|
||||||
60: { "server_id": "keyboard" },
|
60: { "server_id": "keyboard" },
|
||||||
61: { "server_id": "piano"} ,
|
61: { "server_id": "piano"} ,
|
||||||
62: { "server_id": "upright bass"} ,
|
62: { "server_id": "double bass"} ,
|
||||||
70: { "server_id": "voice" },
|
70: { "server_id": "voice" },
|
||||||
80: { "server_id": "flute" },
|
80: { "server_id": "flute" },
|
||||||
90: { "server_id": "clarinet" },
|
90: { "server_id": "clarinet" },
|
||||||
|
|
@ -283,10 +286,21 @@
|
||||||
|
|
||||||
context.JK.instrument_id_to_instrument = {};
|
context.JK.instrument_id_to_instrument = {};
|
||||||
|
|
||||||
|
context.JK.server_to_client_instrument_alpha = [];
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
$.each(context.JK.server_to_client_instrument_map, function(key, value) {
|
$.each(context.JK.server_to_client_instrument_map, function(key, value) {
|
||||||
context.JK.instrument_id_to_instrument[value.server_id] = { client_id: value.client_id, display: key }
|
context.JK.instrument_id_to_instrument[value.server_id] = { client_id: value.client_id, display: key }
|
||||||
|
context.JK.server_to_client_instrument_alpha.push({ client_id: value.client_id, display: key, server_id: value.server_id })
|
||||||
});
|
});
|
||||||
|
|
||||||
|
context.JK.server_to_client_instrument_alpha.sort(function(a, b){
|
||||||
|
if ( a.display < b.display )
|
||||||
|
return -1;
|
||||||
|
if ( a.display > b.display )
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function subtlePulse($element) {
|
function subtlePulse($element) {
|
||||||
$element.find('.bt-content').pulse({'background-color' : '#868686'}, {pulses: 3}, function() { $element.css('background-color', '#980006')})
|
$element.find('.bt-content').pulse({'background-color' : '#868686'}, {pulses: 2, duration: 1000, interval:300}, function() { $element.css('background-color', '#980006')})
|
||||||
}
|
}
|
||||||
|
|
||||||
helpBubble.rotateJamTrackLandingBubbles = function($preview, $video, $ctaButton, $alternativeCta) {
|
helpBubble.rotateJamTrackLandingBubbles = function($preview, $video, $ctaButton, $alternativeCta) {
|
||||||
|
|
@ -203,7 +203,7 @@
|
||||||
helpBubble.showBuyTestDrive = function($element, $offsetParent, user, callback) {
|
helpBubble.showBuyTestDrive = function($element, $offsetParent, user, callback) {
|
||||||
return context.JK.onceBubble($element, 'side-buy-test-drive', user, {offsetParent:$offsetParent, width:260, positions:['right'], postShow: function(container) {
|
return context.JK.onceBubble($element, 'side-buy-test-drive', user, {offsetParent:$offsetParent, width:260, positions:['right'], postShow: function(container) {
|
||||||
subtlePulse(container)
|
subtlePulse(container)
|
||||||
var $bookNow = container('a.book-now')
|
var $bookNow = container.find('a.book-now')
|
||||||
$bookNow.off('click').on('click', function(e) {
|
$bookNow.off('click').on('click', function(e) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
callback()
|
callback()
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ AttachmentStore = context.AttachmentStore
|
||||||
|
|
||||||
notationUploadDone: () ->
|
notationUploadDone: () ->
|
||||||
logger.debug("AttachmentStatus: notationUploadDone")
|
logger.debug("AttachmentStatus: notationUploadDone")
|
||||||
context.JK.Banner.showNotice('Notation Uploaded', 'The music notation file has been uploaded, and can be accessed from the Messages window for this lesson.')
|
#context.JK.Banner.showNotice('Notation Uploaded', 'The music notation file has been uploaded, and can be accessed from the Messages window for this lesson.')
|
||||||
|
|
||||||
notationUploadFail: () ->
|
notationUploadFail: () ->
|
||||||
logger.debug("AttachmentStatus: notationUploadFail")
|
logger.debug("AttachmentStatus: notationUploadFail")
|
||||||
|
|
@ -32,11 +32,11 @@ AttachmentStore = context.AttachmentStore
|
||||||
audioSelected: (e) ->
|
audioSelected: (e) ->
|
||||||
files = $(e.target).get(0).files
|
files = $(e.target).get(0).files
|
||||||
logger.debug("audio files selected: ", files)
|
logger.debug("audio files selected: ", files)
|
||||||
window.AttachmentActions.uploadAudio.trigger(files, @notationUploadDone, @notationUploadFail)
|
window.AttachmentActions.uploadAudios.trigger(files, @notationUploadDone, @notationUploadFail)
|
||||||
|
|
||||||
audioUploadDone: () ->
|
audioUploadDone: () ->
|
||||||
logger.debug("AttachmentStatus: audioUploadDone")
|
logger.debug("AttachmentStatus: audioUploadDone")
|
||||||
context.JK.Banner.showNotice('Audio file Uploaded', 'The audio file has been uploaded, and can be accessed from the Messages window for this lesson.')
|
#context.JK.Banner.showNotice('Audio file Uploaded', 'The audio file has been uploaded, and can be accessed from the Messages window for this lesson.')
|
||||||
|
|
||||||
audioUploadFail: () ->
|
audioUploadFail: () ->
|
||||||
logger.debug("AttachmentStatus: audioUploadFail")
|
logger.debug("AttachmentStatus: audioUploadFail")
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
|
||||||
context = window
|
context = window
|
||||||
rest = context.JK.Rest()
|
rest = context.JK.Rest()
|
||||||
logger = context.JK.logger
|
logger = context.JK.logger
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,7 @@ ChatActions = @ChatActions
|
||||||
else
|
else
|
||||||
purpose = null
|
purpose = null
|
||||||
|
|
||||||
|
additional = null
|
||||||
if msg.purpose == 'Notation File'
|
if msg.purpose == 'Notation File'
|
||||||
additional = `<a className="additional" onClick={this.notationClicked.bind(this, msg.music_notation)}>{msg.music_notation.file_name}</a>`
|
additional = `<a className="additional" onClick={this.notationClicked.bind(this, msg.music_notation)}>{msg.music_notation.file_name}</a>`
|
||||||
else if msg.purpose == 'Audio File'
|
else if msg.purpose == 'Audio File'
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ logger = context.JK.logger
|
||||||
for object in this.props.sourceObjects
|
for object in this.props.sourceObjects
|
||||||
nm = "check_#{object.id}"
|
nm = "check_#{object.id}"
|
||||||
checked = @isChecked(object.id)
|
checked = @isChecked(object.id)
|
||||||
object_options.push `<div className='checkItem'><input type='checkbox' key={object.id} name={nm} data-object-id={object.id} checked={checked}></input><label htmlFor={nm}>{object.description}</label></div>`
|
object_options.push `<div className='checkItem'><input type='checkbox' key={object.id} name={nm} data-object-id={object.id} checked={checked}></input><label htmlFor={nm}>{object.description}</label><br className="clearall"/></div>`
|
||||||
|
|
||||||
`<div className="CheckBoxList react-component">
|
`<div className="CheckBoxList react-component">
|
||||||
<div className="checkbox-scroller left">
|
<div className="checkbox-scroller left">
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,8 @@ ConfigureTracksStore = @ConfigureTracksStore
|
||||||
|
|
||||||
instruments = []
|
instruments = []
|
||||||
instruments.push(`<option key="" value="">Select the instrument for this track</option>`)
|
instruments.push(`<option key="" value="">Select the instrument for this track</option>`)
|
||||||
for displayName, value of context.JK.server_to_client_instrument_map
|
for instrument in context.JK.server_to_client_instrument_alpha
|
||||||
instruments.push(`<option key={value.server_id} value={value.server_id}>{displayName}</option>`)
|
instruments.push(`<option key={instrument.server_id} value={instrument.server_id}>{instrument.display}</option>`)
|
||||||
|
|
||||||
vsts = []
|
vsts = []
|
||||||
|
|
||||||
|
|
@ -134,8 +134,8 @@ ConfigureTracksStore = @ConfigureTracksStore
|
||||||
midiInstruments = []
|
midiInstruments = []
|
||||||
|
|
||||||
instruments = []
|
instruments = []
|
||||||
for displayName, value of context.JK.server_to_client_instrument_map
|
for instrument in context.JK.server_to_client_instrument_alpha
|
||||||
instruments.push(`<option key={value.server_id} value={value.server_id}>{displayName}</option>`)
|
instruments.push(`<option key={instrument.server_id} value={instrument.server_id}>{instrument.display}</option>`)
|
||||||
|
|
||||||
selectedMidiInterface = ''
|
selectedMidiInterface = ''
|
||||||
selectedInstrument = context.JK.client_to_server_instrument_map[50].server_id # default to electric guitar
|
selectedInstrument = context.JK.client_to_server_instrument_map[50].server_id # default to electric guitar
|
||||||
|
|
|
||||||
|
|
@ -129,9 +129,9 @@ LessonTimerActions = context.LessonTimerActions
|
||||||
rest.checkLessonReschedule({id: lesson.id, update_all: recurring})
|
rest.checkLessonReschedule({id: lesson.id, update_all: recurring})
|
||||||
.done((response) => (
|
.done((response) => (
|
||||||
if recurring
|
if recurring
|
||||||
window.location.href = '/client#/jamclass/lesson-booking/' + lesson.lesson_booking_id
|
window.location.href = '/client#/jamclass/lesson-booking/' + lesson.lesson_booking_id + "_rescheduling"
|
||||||
else
|
else
|
||||||
window.location.href = '/client#/jamclass/lesson-booking/' + lesson.id
|
window.location.href = '/client#/jamclass/lesson-booking/' + lesson.id + "_rescheduling"
|
||||||
))
|
))
|
||||||
.fail((jqXHR) => (
|
.fail((jqXHR) => (
|
||||||
if jqXHR.status == 422
|
if jqXHR.status == 422
|
||||||
|
|
@ -179,7 +179,13 @@ LessonTimerActions = context.LessonTimerActions
|
||||||
context.JK.Banner.showAlert('late cancellation warning',
|
context.JK.Banner.showAlert('late cancellation warning',
|
||||||
'Cancelling a lesson less than 24 hours before it’s scheduled to start should be avoided, as it’s an inconvenience to the student. Repeated violations of this policy will negatively affect your teacher score.')
|
'Cancelling a lesson less than 24 hours before it’s scheduled to start should be avoided, as it’s an inconvenience to the student. Repeated violations of this policy will negatively affect your teacher score.')
|
||||||
|
|
||||||
@refreshLesson(lesson.id)
|
lessonsFromBooking = []
|
||||||
|
for check in @lessons()
|
||||||
|
if check.lesson_booking_id == lesson.lesson_booking_id
|
||||||
|
lessonsFromBooking.push(check)
|
||||||
|
|
||||||
|
for check in lessonsFromBooking
|
||||||
|
@refreshLesson(check.id)
|
||||||
|
|
||||||
cancelLessonBookingFail: (jqXHR) ->
|
cancelLessonBookingFail: (jqXHR) ->
|
||||||
@app.ajaxError(jqXHR)
|
@app.ajaxError(jqXHR)
|
||||||
|
|
@ -224,12 +230,12 @@ LessonTimerActions = context.LessonTimerActions
|
||||||
if lesson.recurring
|
if lesson.recurring
|
||||||
buttons = []
|
buttons = []
|
||||||
buttons.push({
|
buttons.push({
|
||||||
name: 'THIS SESSION',
|
name: 'THIS LESSON',
|
||||||
buttonStyle: 'button-orange',
|
buttonStyle: 'button-orange',
|
||||||
click: (() => (@rescheduleSelected(lesson, false)))
|
click: (() => (@rescheduleSelected(lesson, false)))
|
||||||
})
|
})
|
||||||
buttons.push({
|
buttons.push({
|
||||||
name: 'ALL SESSIONS',
|
name: 'ALL LESSONS',
|
||||||
buttonStyle: 'button-orange',
|
buttonStyle: 'button-orange',
|
||||||
click: (() => (@rescheduleSelected(lesson, true)))
|
click: (() => (@rescheduleSelected(lesson, true)))
|
||||||
})
|
})
|
||||||
|
|
@ -251,13 +257,13 @@ LessonTimerActions = context.LessonTimerActions
|
||||||
verbLower = 'cancel'
|
verbLower = 'cancel'
|
||||||
if !lesson.isRequested || lesson.recurring
|
if !lesson.isRequested || lesson.recurring
|
||||||
buttons = []
|
buttons = []
|
||||||
buttons.push({name: 'CANCEL', buttonStyle: 'button-grey'})
|
buttons.push({name: 'CLOSE', buttonStyle: 'button-grey'})
|
||||||
buttons.push({
|
buttons.push({
|
||||||
name: 'THIS SESSION',
|
name: 'CANCEL THIS LESSON',
|
||||||
buttonStyle: 'button-orange',
|
buttonStyle: 'button-orange',
|
||||||
click: (() => (@cancelSelected(lesson, false)))
|
click: (() => (@cancelSelected(lesson, false)))
|
||||||
})
|
})
|
||||||
buttons.push({name: 'ALL SESSIONS', buttonStyle: 'button-orange', click: (() => (@cancelSelected(lesson, true)))})
|
buttons.push({name: 'CANCEL ALL LESSONS', buttonStyle: 'button-orange', click: (() => (@cancelSelected(lesson, true)))})
|
||||||
context.JK.Banner.show({
|
context.JK.Banner.show({
|
||||||
title: 'Select One',
|
title: 'Select One',
|
||||||
html: "Do you wish to all #{verbLower} all lessons or just the one selected?",
|
html: "Do you wish to all #{verbLower} all lessons or just the one selected?",
|
||||||
|
|
@ -521,7 +527,10 @@ LessonTimerActions = context.LessonTimerActions
|
||||||
else
|
else
|
||||||
unreadMessages = null
|
unreadMessages = null
|
||||||
|
|
||||||
timeStmt = lessonData.music_session.pretty_scheduled_start_with_timezone
|
if lessonData.status == 'countered'
|
||||||
|
timeStmt = lessonData.counter_slot.pretty_scheduled_start_with_timezone
|
||||||
|
else
|
||||||
|
timeStmt = lessonData.music_session.pretty_scheduled_start_with_timezone
|
||||||
|
|
||||||
if lessonData.times? && lessonData.displayStatus == 'Scheduled'
|
if lessonData.times? && lessonData.displayStatus == 'Scheduled'
|
||||||
if lessonData.times.startingSoon
|
if lessonData.times.startingSoon
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,135 @@
|
||||||
|
@JamClassSearchHeader = React.createClass({
|
||||||
|
|
||||||
|
mixins: [Reflux.listenTo(@UserStore, "onUserChanged"), Reflux.listenTo(@TeacherSearchStore, "onTeacherSearchStore")]
|
||||||
|
|
||||||
|
onTeacherSearchStore: ()->
|
||||||
|
|
||||||
|
getInitialState: () ->
|
||||||
|
{user: null}
|
||||||
|
|
||||||
|
|
||||||
|
onUserChanged: (@user) ->
|
||||||
|
@setState({user: @user?.user})
|
||||||
|
|
||||||
|
|
||||||
|
createSearchDescription: () ->
|
||||||
|
searchOptions = TeacherSearchStore.getState()
|
||||||
|
|
||||||
|
summary = ''
|
||||||
|
|
||||||
|
if searchOptions.onlyMySchool && @state.user?.school_id?
|
||||||
|
summary += "From My School Only"
|
||||||
|
|
||||||
|
instruments = searchOptions.instruments
|
||||||
|
if instruments? && instruments.length > 0
|
||||||
|
if instruments.length == 1
|
||||||
|
bit = "Instrument = #{InstrumentStore.display(instruments[0])}"
|
||||||
|
else
|
||||||
|
instruments.length > 1
|
||||||
|
bit = "Instruments = #{InstrumentStore.display(instruments[0])} ... "
|
||||||
|
if summary.length > 0
|
||||||
|
summary += ', '
|
||||||
|
summary += " #{bit}"
|
||||||
|
|
||||||
|
subjects = searchOptions.subjects
|
||||||
|
if subjects? && subjects.length > 0
|
||||||
|
if subjects.length == 1
|
||||||
|
bit = "Subject = #{SubjectStore.display(subjects[0])}"
|
||||||
|
else
|
||||||
|
subjects.length > 1
|
||||||
|
bit = "Subjects = #{SubjectStore.display(subjects[0])} ... "
|
||||||
|
if summary.length > 0
|
||||||
|
summary += ', '
|
||||||
|
summary += " #{bit}"
|
||||||
|
|
||||||
|
genres = searchOptions.genres
|
||||||
|
if genres? && genres.length > 0
|
||||||
|
if genres.length == 1
|
||||||
|
bit = "Genre = #{GenreStore.display(genres[0])}"
|
||||||
|
else
|
||||||
|
genres.length > 1
|
||||||
|
bit = "Genres = #{GenreStore.display(genres[0])} ... "
|
||||||
|
if summary.length > 0
|
||||||
|
summary += ', '
|
||||||
|
summary += " #{bit}"
|
||||||
|
|
||||||
|
languages = searchOptions.languages
|
||||||
|
if languages? && languages.length > 0
|
||||||
|
if languages.length == 1
|
||||||
|
bit = "Language = #{LanguageStore.display(languages[0])}"
|
||||||
|
else
|
||||||
|
languages.length > 1
|
||||||
|
bit = "Languages = #{LanguageStore.display(languages[0])} ... "
|
||||||
|
if summary.length > 0
|
||||||
|
summary += ', '
|
||||||
|
summary += " #{bit}"
|
||||||
|
|
||||||
|
if searchOptions.teaches_beginner || searchOptions.teaches_intermediate || searchOptions.teaches_advanced
|
||||||
|
bit = "Teaches "
|
||||||
|
qualifier = ''
|
||||||
|
if searchOptions.teaches_beginner
|
||||||
|
qualifier += "Beginner"
|
||||||
|
if searchOptions.teaches_intermediate
|
||||||
|
if qualifier.length > 0
|
||||||
|
qualifier += ", "
|
||||||
|
qualifier += "Intermediate"
|
||||||
|
if searchOptions.teaches_advanced
|
||||||
|
if qualifier.length > 0
|
||||||
|
qualifier += ", "
|
||||||
|
qualifier += "Advanced"
|
||||||
|
if summary.length > 0
|
||||||
|
summary += ', '
|
||||||
|
summary += " #{bit}#{qualifier}"
|
||||||
|
|
||||||
|
if searchOptions.student_age?
|
||||||
|
if summary.length > 0
|
||||||
|
summary += ', '
|
||||||
|
summary += "Student Age = #{searchOptions.student_age}"
|
||||||
|
|
||||||
|
if searchOptions.years_teaching?
|
||||||
|
if summary.length > 0
|
||||||
|
summary += ', '
|
||||||
|
summary += "Years Teaching = #{searchOptions.years_teaching}"
|
||||||
|
|
||||||
|
if searchOptions.location?.country?
|
||||||
|
if summary.length > 0
|
||||||
|
summary += ', '
|
||||||
|
summary += "Country = #{searchOptions.location.country}"
|
||||||
|
|
||||||
|
if searchOptions.location?.region?
|
||||||
|
|
||||||
|
if summary.length > 0
|
||||||
|
summary += ', '
|
||||||
|
summary += "Region = #{searchOptions.location.region}"
|
||||||
|
|
||||||
|
|
||||||
|
if summary.length == 0
|
||||||
|
summary = 'all teachers'
|
||||||
|
|
||||||
|
summary
|
||||||
|
|
||||||
|
|
||||||
|
render: () ->
|
||||||
|
searchDesc = @createSearchDescription()
|
||||||
|
|
||||||
|
if @props.teacher
|
||||||
|
complete = `<span className="search-results-options">
|
||||||
|
<a href="/client#/teachers/search" className="results-text link">Search Results</a> :
|
||||||
|
<span className="teacher-name">{this.props.teacher.name}</span>
|
||||||
|
</span>`
|
||||||
|
else
|
||||||
|
complete =
|
||||||
|
`<span className="search-results-options">
|
||||||
|
<span className="search-description">
|
||||||
|
<span className="results-text">Search Results / </span>
|
||||||
|
<span className="search-summary">{searchDesc}</span>
|
||||||
|
</span>
|
||||||
|
</span>`
|
||||||
|
`<div className="jamclass-search-header">
|
||||||
|
<a href="/client#/home">JamKazam Home</a> :
|
||||||
|
<a href="/client#/jamclass">JamClass Home</a> :
|
||||||
|
<a className="teacher-search-options" href="/client#/jamclass/searchOptions">Teachers Search</a><span
|
||||||
|
className="teacher-quote"> : </span>
|
||||||
|
{complete}
|
||||||
|
</div>`
|
||||||
|
})
|
||||||
|
|
@ -50,6 +50,44 @@ UserStore = context.UserStore
|
||||||
slot.creatorRoleRelative = "your"
|
slot.creatorRoleRelative = "your"
|
||||||
slot.mySlot = @mySlot(slot)
|
slot.mySlot = @mySlot(slot)
|
||||||
|
|
||||||
|
processBooking:(booking) ->
|
||||||
|
booking.neverAccepted = booking.accepter_id?
|
||||||
|
booking.isCounter = booking.counter_slot? && booking.status != 'canceled' && booking.status != 'suspended'
|
||||||
|
booking.studentViewing = booking.user_id == context.JK.currentUserId
|
||||||
|
booking.teacherViewing = !booking.studentViewing
|
||||||
|
booking.isRequested = booking.status == 'requested' && !booking.isCounter
|
||||||
|
booking.isCanceled = booking.status == 'canceled'
|
||||||
|
booking.isSuspended = booking.status == 'suspended'
|
||||||
|
if booking.isCounter
|
||||||
|
if booking.counter_slot['is_teacher_created?']
|
||||||
|
booking.countererId = booking.teacher_id
|
||||||
|
else
|
||||||
|
booking.countererId = booking.user_id
|
||||||
|
|
||||||
|
selfLastToAct = false
|
||||||
|
if booking.isRequested
|
||||||
|
selfLastToAct = booking.studentViewing
|
||||||
|
else if booking.isCounter
|
||||||
|
selfLastToAct = booking.countererId == context.JK.currentUserId
|
||||||
|
else if booking.isCanceled
|
||||||
|
selfLastToAct = booking.canceler_id == context.JK.currentUserId
|
||||||
|
else if booking.isSuspended
|
||||||
|
selfLastToAct = booking.studentViewing
|
||||||
|
else
|
||||||
|
selfLastToAct = false
|
||||||
|
booking.selfLastToAct = selfLastToAct
|
||||||
|
|
||||||
|
multipleOptions = false
|
||||||
|
if booking.neverAccepted
|
||||||
|
multipleOptions = !(!booking.isCounter && booking.selfLastToAct)
|
||||||
|
else if booking.isCounter
|
||||||
|
multipleOptions = !booking.selfLastToAct
|
||||||
|
else
|
||||||
|
multipleOptions = false
|
||||||
|
onlyOption = !multipleOptions
|
||||||
|
booking.onlyOption = onlyOption
|
||||||
|
|
||||||
|
#nextProps.slot_decision = 'counter'
|
||||||
componentWillUpdate: (nextProps, nextState) ->
|
componentWillUpdate: (nextProps, nextState) ->
|
||||||
if nextState.booking?
|
if nextState.booking?
|
||||||
booking = nextState.booking
|
booking = nextState.booking
|
||||||
|
|
@ -61,6 +99,35 @@ UserStore = context.UserStore
|
||||||
@processSlot(booking.default_slot, booking)
|
@processSlot(booking.default_slot, booking)
|
||||||
@processSlot(booking.alt_slot, booking)
|
@processSlot(booking.alt_slot, booking)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
onlyOption:() ->
|
||||||
|
#(this.props.initial && !this.props.selfLastToAct )|| !(this.props.counter && !this.props.selfLastToAct)
|
||||||
|
|
||||||
|
#@initialRequestSlotsVisible() || !@counteredSlotVisible()
|
||||||
|
#(this.props.initial && this.props.selfLastToAct ) || !(this.props.counter && !this.props.selfLastToAct)
|
||||||
|
#(@neverAccepted() && @selfLastToAct()) || !(@isCounter() && !@selfLastToAct())
|
||||||
|
!@multipleOptions()
|
||||||
|
|
||||||
|
multipleOptions: () ->
|
||||||
|
if @neverAccepted()
|
||||||
|
!(!@isCounter() && @selfLastToAct())
|
||||||
|
else if this.props.counter
|
||||||
|
!@selfLastToAct()
|
||||||
|
else
|
||||||
|
false
|
||||||
|
initialRequestSlotsVisible: () ->
|
||||||
|
console.log("initialRequestSlotsVisible: " + this.neverAccepted() )
|
||||||
|
#this.neverAccepted() && this.selfLastToAct()
|
||||||
|
|
||||||
|
# is there a counter slot showing
|
||||||
|
counteredSlotVisible: () ->
|
||||||
|
console.log("isCounter " + this.isCounter() + ", this.selfLastToAct()" + this.selfLastToAct())
|
||||||
|
this.isCounter() && !this.selfLastToAct()
|
||||||
|
|
||||||
|
counterSlotVisible: () ->
|
||||||
|
true
|
||||||
|
|
||||||
getInitialState: () ->
|
getInitialState: () ->
|
||||||
{
|
{
|
||||||
user: null,
|
user: null,
|
||||||
|
|
@ -73,14 +140,28 @@ UserStore = context.UserStore
|
||||||
|
|
||||||
beforeShow: (e) ->
|
beforeShow: (e) ->
|
||||||
|
|
||||||
|
parseId: (id) ->
|
||||||
|
result = {purpose: null}
|
||||||
|
|
||||||
|
bits = id.split('_')
|
||||||
|
if bits.length == 1
|
||||||
|
result.id = id
|
||||||
|
else if bits.length > 1
|
||||||
|
result.id =bits[0]
|
||||||
|
result.purpose = bits[1]
|
||||||
|
else
|
||||||
|
result.id = id
|
||||||
|
result
|
||||||
|
|
||||||
afterShow: (e) ->
|
afterShow: (e) ->
|
||||||
@setState({updating: true, counterErrors: null, cancelErrors: null})
|
parsed = @parseId(e.id)
|
||||||
|
@setState({updating: true, counterErrors: null, cancelErrors: null, purpose: parsed.purpose})
|
||||||
rest.getLessonBooking({
|
rest.getLessonBooking({
|
||||||
id: e.id,
|
id: parsed.id,
|
||||||
}).done((response) => @getLessonBookingDone(response)).fail(@app.ajaxError)
|
}).done((response) => @getLessonBookingDone(response)).fail(@app.ajaxError)
|
||||||
|
|
||||||
hasFocusedLesson: () ->
|
hasFocusedLesson: () ->
|
||||||
this.state.booking.focused_lesson?.id?
|
@focusedLesson()?
|
||||||
|
|
||||||
focusedLesson: () ->
|
focusedLesson: () ->
|
||||||
this.state?.booking?.focused_lesson
|
this.state?.booking?.focused_lesson
|
||||||
|
|
@ -105,6 +186,14 @@ UserStore = context.UserStore
|
||||||
#booking.next_lesson.lesson_booking = booking
|
#booking.next_lesson.lesson_booking = booking
|
||||||
@postProcessLesson(booking.next_lesson)
|
@postProcessLesson(booking.next_lesson)
|
||||||
|
|
||||||
|
@processBooking(booking)
|
||||||
|
|
||||||
|
|
||||||
|
if booking.onlyOption
|
||||||
|
# you see two options (accept, and propose new) if it's a counter and you are not the last to act
|
||||||
|
# the only choice possible in most cases is to propose a new time
|
||||||
|
#nextState.slot_decision = 'counter'
|
||||||
|
startSlotDecision = 'counter'
|
||||||
@setState({booking: booking, updating: false, slot_decision: startSlotDecision, updatingLesson: false, update_all: update_all})
|
@setState({booking: booking, updating: false, slot_decision: startSlotDecision, updatingLesson: false, update_all: update_all})
|
||||||
|
|
||||||
getLessonBookingDone: (response) ->
|
getLessonBookingDone: (response) ->
|
||||||
|
|
@ -213,7 +302,8 @@ UserStore = context.UserStore
|
||||||
minute = $slot.find('.minute').val()
|
minute = $slot.find('.minute').val()
|
||||||
am_pm = $slot.find('.am_pm').val()
|
am_pm = $slot.find('.am_pm').val()
|
||||||
|
|
||||||
update_all = $slot.find('input.update-all').is(':checked') && @isRecurring()
|
#update_all = $slot.find('input.update-all').is(':checked') && @isRecurring()
|
||||||
|
update_all = @state.update_all && @isRecurring()
|
||||||
|
|
||||||
if hour? and hour != ''
|
if hour? and hour != ''
|
||||||
hour = new Number(hour)
|
hour = new Number(hour)
|
||||||
|
|
@ -227,7 +317,7 @@ UserStore = context.UserStore
|
||||||
else
|
else
|
||||||
minute = null
|
minute = null
|
||||||
|
|
||||||
if !@isRecurring()
|
if !update_all
|
||||||
date = picker.datepicker("getDate")
|
date = picker.datepicker("getDate")
|
||||||
if date?
|
if date?
|
||||||
date = context.JK.formatDateYYYYMMDD(date)
|
date = context.JK.formatDateYYYYMMDD(date)
|
||||||
|
|
@ -399,10 +489,10 @@ UserStore = context.UserStore
|
||||||
text = "Preferred day/time for lesson is #{this.slotTime(defaultSlot)}. Secondary option is #{this.slotTime(altSlot)}."
|
text = "Preferred day/time for lesson is #{this.slotTime(defaultSlot)}. Secondary option is #{this.slotTime(altSlot)}."
|
||||||
|
|
||||||
slotTime: (slot, booking = this.state.booking) ->
|
slotTime: (slot, booking = this.state.booking) ->
|
||||||
if @isRecurring(booking)
|
if @hasFocusedLesson() || !@isRecurring(booking)
|
||||||
"#{this.dayOfWeek(slot)} at #{this.dayTime(slot)}"
|
|
||||||
else
|
|
||||||
slot.pretty_start_time
|
slot.pretty_start_time
|
||||||
|
else
|
||||||
|
"#{this.dayOfWeek(slot)} at #{this.dayTime(slot)}"
|
||||||
|
|
||||||
slotTimePhrase: (slot) ->
|
slotTimePhrase: (slot) ->
|
||||||
if @isRecurring()
|
if @isRecurring()
|
||||||
|
|
@ -704,9 +794,10 @@ UserStore = context.UserStore
|
||||||
|
|
||||||
renderStudentRequested: () ->
|
renderStudentRequested: () ->
|
||||||
`<div className="contents">
|
`<div className="contents">
|
||||||
<div className="row">
|
<div className="row request-sent">
|
||||||
{this.userHeader(this.myself())}
|
{this.userHeader(this.myself())}
|
||||||
Your request has been sent. You will receive an email when {this.teacher().name} responds.
|
Your request has been sent. You will receive an email when {this.teacher().name} responds.
|
||||||
|
{this.createDetail()}
|
||||||
</div>
|
</div>
|
||||||
<LessonBookingDecision {...this.decisionProps([])} />
|
<LessonBookingDecision {...this.decisionProps([])} />
|
||||||
</div>`
|
</div>`
|
||||||
|
|
@ -719,6 +810,22 @@ UserStore = context.UserStore
|
||||||
updateCreditCard: (e) ->
|
updateCreditCard: (e) ->
|
||||||
window.location.href="/client#/account/paymentHistory"
|
window.location.href="/client#/account/paymentHistory"
|
||||||
|
|
||||||
|
createDetail: () ->
|
||||||
|
if @hasFocusedLesson() || !@isRecurring()
|
||||||
|
if @onlyOption() && @rescheduling()
|
||||||
|
detail = `<p className="proposing-new-time">You are proposing to change the date/time of the lesson currently scheduled for {this.slotTime(this.state.booking.default_slot)}</p>`
|
||||||
|
else
|
||||||
|
detail = `<p className="generic-time-stmt">Your {this.lessonDesc()} will take place this {this.slotTime(this.state.booking.default_slot)}</p>`
|
||||||
|
else
|
||||||
|
if @onlyOption() && @rescheduling()
|
||||||
|
detail = `<p className="proposing-new-time">You are proposing to change the date/time of the lesson currently scheduled for {this.slotTime(this.state.booking.default_slot)}</p>`
|
||||||
|
else
|
||||||
|
detail = `<p className="generic-time-stmt">Your {this.lessonDesc()} will take place each {this.slotTime(this.state.booking.default_slot)}</p>`
|
||||||
|
detail
|
||||||
|
|
||||||
|
rescheduling: () ->
|
||||||
|
@state.purpose == 'rescheduling'
|
||||||
|
|
||||||
renderStudentComplete: () ->
|
renderStudentComplete: () ->
|
||||||
@renderStudentApproved()
|
@renderStudentApproved()
|
||||||
|
|
||||||
|
|
@ -746,10 +853,7 @@ UserStore = context.UserStore
|
||||||
if @studentMadeDefaultSlot()
|
if @studentMadeDefaultSlot()
|
||||||
message = this.slotMessage(this.state.booking.default_slot, 'accept')
|
message = this.slotMessage(this.state.booking.default_slot, 'accept')
|
||||||
|
|
||||||
if @isRecurring()
|
detail = @createDetail()
|
||||||
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">
|
summary = `<div className="row">
|
||||||
{this.userHeader(this.teacher())}
|
{this.userHeader(this.teacher())}
|
||||||
<p>Has accepted your lesson request.</p>
|
<p>Has accepted your lesson request.</p>
|
||||||
|
|
@ -766,10 +870,7 @@ UserStore = context.UserStore
|
||||||
if @studentMadeDefaultSlot()
|
if @studentMadeDefaultSlot()
|
||||||
message = this.slotMessage(this.state.booking.default_slot, 'accept')
|
message = this.slotMessage(this.state.booking.default_slot, 'accept')
|
||||||
|
|
||||||
if @isRecurring()
|
detail = @createDetail()
|
||||||
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">
|
summary = `<div className="row">
|
||||||
{this.userHeader(this.teacher())}
|
{this.userHeader(this.teacher())}
|
||||||
<p>Has accepted your lesson request.</p>
|
<p>Has accepted your lesson request.</p>
|
||||||
|
|
@ -780,10 +881,7 @@ UserStore = context.UserStore
|
||||||
if @studentMadeDefaultSlot()
|
if @studentMadeDefaultSlot()
|
||||||
message = this.slotMessage(this.state.booking.default_slot, 'accept')
|
message = this.slotMessage(this.state.booking.default_slot, 'accept')
|
||||||
|
|
||||||
if @isRecurring()
|
detail = @createDetail()
|
||||||
detail = `<p className="lesson-time">Your {this.lessonDesc()} will take place each {this.slotTime(this.state.booking.default_slot)}</p>`
|
|
||||||
else
|
|
||||||
detail = `<p className="lesson-time">Your {this.lessonDesc()} will take place this {this.slotTime(this.state.booking.default_slot)}</p>`
|
|
||||||
|
|
||||||
summary = `<div className="row">
|
summary = `<div className="row">
|
||||||
{this.userHeader(this.teacher())}
|
{this.userHeader(this.teacher())}
|
||||||
|
|
@ -847,10 +945,7 @@ UserStore = context.UserStore
|
||||||
if @studentMadeDefaultSlot()
|
if @studentMadeDefaultSlot()
|
||||||
message = this.slotMessage(this.state.booking.default_slot, 'accept')
|
message = this.slotMessage(this.state.booking.default_slot, 'accept')
|
||||||
|
|
||||||
if @isRecurring()
|
detail = @createDetail()
|
||||||
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">
|
summary = `<div className="row">
|
||||||
{this.userHeader(this.student())}
|
{this.userHeader(this.student())}
|
||||||
<p>Is ready to take the lesson.</p>
|
<p>Is ready to take the lesson.</p>
|
||||||
|
|
@ -869,10 +964,7 @@ UserStore = context.UserStore
|
||||||
if @studentMadeDefaultSlot()
|
if @studentMadeDefaultSlot()
|
||||||
message = this.slotMessage(this.state.booking.default_slot, 'accept')
|
message = this.slotMessage(this.state.booking.default_slot, 'accept')
|
||||||
|
|
||||||
if @isRecurring()
|
detail = @createDetail()
|
||||||
detail = `<p className="lesson-time">Your {this.lessonDesc()} will take place each {this.slotTime(this.state.booking.default_slot)}</p>`
|
|
||||||
else
|
|
||||||
detail = `<p className="lesson-time">Your {this.lessonDesc()} will take place this {this.slotTime(this.state.booking.default_slot)}</p>`
|
|
||||||
|
|
||||||
summary = `<div className="row">
|
summary = `<div className="row">
|
||||||
{this.userHeader(this.teacher())}
|
{this.userHeader(this.teacher())}
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,11 @@
|
||||||
@checkboxes = [{selector: 'input.slot-decision', stateKey: 'slot-decision'}, {selector: 'input.update-all', propsKey: 'update_all'}]
|
@checkboxes = [{selector: 'input.slot-decision', stateKey: 'slot-decision'}, {selector: 'input.update-all', propsKey: 'update_all'}]
|
||||||
@root = $(@getDOMNode())
|
@root = $(@getDOMNode())
|
||||||
@iCheckify()
|
@iCheckify()
|
||||||
|
@slotDate = @root.find('.date-picker')
|
||||||
|
@slotDate.datepicker({
|
||||||
|
dateFormat: "D M d yy",
|
||||||
|
onSelect: ((e) => @toggleDate(e))
|
||||||
|
})
|
||||||
|
|
||||||
componentDidUpdate: () ->
|
componentDidUpdate: () ->
|
||||||
@iCheckify()
|
@iCheckify()
|
||||||
|
|
@ -66,6 +71,14 @@
|
||||||
onSelect: ((e) => @toggleDate(e))
|
onSelect: ((e) => @toggleDate(e))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
toggleDate: (e) ->
|
||||||
|
|
||||||
|
componentWillReceiveProps: (nextProps) ->
|
||||||
|
if @onlyOption()
|
||||||
|
console.log("setting it counter")
|
||||||
|
# if this isn't a counter situation, then there is no 'Accept their time', so there should only be one radio button, and we'll select that value already
|
||||||
|
@setState({"slot-decision": "counter"})
|
||||||
|
|
||||||
checkboxChanged: (e) ->
|
checkboxChanged: (e) ->
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -126,10 +139,21 @@
|
||||||
|
|
||||||
nullOp: ()->
|
nullOp: ()->
|
||||||
|
|
||||||
|
onlyOption: () ->
|
||||||
|
# (!this.props.initial && this.props.selfLastToAct ) || !(this.props.counter && !this.props.selfLastToAct)
|
||||||
|
!@multipleOptions()
|
||||||
|
|
||||||
|
multipleOptions: () ->
|
||||||
|
if this.props.initial
|
||||||
|
!(!this.props.counter && this.props.selfLastToAct)
|
||||||
|
else if this.props.counter
|
||||||
|
!this.props.selfLastToAct
|
||||||
|
else
|
||||||
|
false
|
||||||
|
|
||||||
render: () ->
|
render: () ->
|
||||||
|
|
||||||
|
#showUpdateAll = !this.props.initial
|
||||||
showUpdateAll = !this.props.initial
|
|
||||||
|
|
||||||
if (!this.props.initial && !this.props.counter) || this.props.selfLastToAct
|
if (!this.props.initial && !this.props.counter) || this.props.selfLastToAct
|
||||||
userPromptHeader = `<h3>Would you like to change this lesson?</h3>`
|
userPromptHeader = `<h3>Would you like to change this lesson?</h3>`
|
||||||
|
|
@ -150,7 +174,7 @@
|
||||||
else
|
else
|
||||||
verb = "CANCEL"
|
verb = "CANCEL"
|
||||||
|
|
||||||
if this.props.update_all && showUpdateAll
|
if this.props.update_all
|
||||||
actionBtnText = "#{verb} ALL LESSONS"
|
actionBtnText = "#{verb} ALL LESSONS"
|
||||||
else
|
else
|
||||||
actionBtnText = "#{verb} LESSON"
|
actionBtnText = "#{verb} LESSON"
|
||||||
|
|
@ -159,13 +183,13 @@
|
||||||
else
|
else
|
||||||
actionBtnText = "ACCEPT & UPDATE LESSON"
|
actionBtnText = "ACCEPT & UPDATE LESSON"
|
||||||
|
|
||||||
counterClasses={field: true, 'slot-decision-field': true, error: this.props.counterErrors?, counterSelected: this.props.slot_decision == 'counter'}
|
counterClasses={field: true, 'slot-decision-field': true, error: this.props.counterErrors?, counterSelected: this.props.slot_decision == 'counter', onlyOption: @onlyOption()}
|
||||||
|
|
||||||
|
|
||||||
if this.props.counterErrors?
|
if this.props.counterErrors?
|
||||||
errorText = window.JK.reactErrors(this.props.counterErrors, {day_of_week: 'Day' })
|
errorText = window.JK.reactErrors(this.props.counterErrors, {day_of_week: 'Day' })
|
||||||
|
|
||||||
if this.props.is_recurring
|
if this.props.is_recurring && this.props.update_all
|
||||||
|
|
||||||
slotAltPrompt = `<div className="slot-alt-prompt">
|
slotAltPrompt = `<div className="slot-alt-prompt">
|
||||||
<span className="alt-date-block">
|
<span className="alt-date-block">
|
||||||
|
|
@ -182,17 +206,18 @@
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
</div>`
|
</div>`
|
||||||
if showUpdateAll
|
#if @props.update_all
|
||||||
updateAllField =
|
# updateAllField =
|
||||||
`<div className="field update-all-field">
|
# `<div className="field update-all-field">
|
||||||
<input disabled={this.props.disabled} className="update-all" type="checkbox" name="update-all" readyOnly="true" onChange={this.nullOp} checked={this.props.update_all} /><label>Update all lessons</label>
|
# <input disabled={this.props.disabled} className="update-all" type="checkbox" name="update-all" readyOnly="true" onChange={this.nullOp} checked={this.props.update_all} /><label>Update all lessons</label>
|
||||||
</div>`
|
# </div>`
|
||||||
|
updateAllField = null
|
||||||
|
|
||||||
else
|
else
|
||||||
slotAltPrompt = `<div className="slot-alt-prompt">
|
slotAltPrompt = `<div className="slot-alt-prompt">
|
||||||
<span className="alt-date-block">
|
<span className="alt-date-block">
|
||||||
<span className="alt-date">Date:</span>
|
<span className="alt-date">Date:</span>
|
||||||
<input className="date-picker" type="text" data-slot={i}></input>
|
<input className="date-picker" name="alt-date-input" type="text" data-slot={i}></input>
|
||||||
</span>
|
</span>
|
||||||
<span className="alt-time-block">
|
<span className="alt-time-block">
|
||||||
<span className="alt-time">Time:</span>
|
<span className="alt-time">Time:</span>
|
||||||
|
|
@ -222,7 +247,7 @@
|
||||||
else
|
else
|
||||||
slotDetail = `<div className="slot-detail">{slot.slotTime}</div>`
|
slotDetail = `<div className="slot-detail">{slot.slotTime}</div>`
|
||||||
|
|
||||||
slots.push(`<div key={slot.id} className="field slot-decision-field">
|
slots.push(`<div key={slot.id} data-slot-id={slot.id} className="field slot-decision-field">
|
||||||
<div className="label-area">
|
<div className="label-area">
|
||||||
<input disabled={this.props.disabled} className="slot-decision" type="radio" name="slot-decision"
|
<input disabled={this.props.disabled} className="slot-decision" type="radio" name="slot-decision"
|
||||||
value={slot.id} readyOnly={true} defaultChecked={slot.id == this.props.slot_decision} /><label>{this.slotLabelText(i, slot)}</label>
|
value={slot.id} readyOnly={true} defaultChecked={slot.id == this.props.slot_decision} /><label>{this.slotLabelText(i, slot)}</label>
|
||||||
|
|
@ -234,6 +259,12 @@
|
||||||
# if you have issued a counter, you should be able to withdraw it
|
# if you have issued a counter, you should be able to withdraw it
|
||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
|
#cancelField = `<div className="field slot-decision-field">
|
||||||
|
# <input disabled={this.props.disabled} className="slot-decision" type="radio" name="slot-decision" value="decline" readyOnly={true} defaultChecked={this.props.slot_decision == 'decline'} /><label>{declineVerb} lesson
|
||||||
|
# request</label>
|
||||||
|
#</div>`
|
||||||
|
cancelField = null
|
||||||
|
|
||||||
`<div className="row">
|
`<div className="row">
|
||||||
<div className="column column-left">
|
<div className="column column-left">
|
||||||
{userPromptHeader}
|
{userPromptHeader}
|
||||||
|
|
@ -247,10 +278,7 @@
|
||||||
</div>
|
</div>
|
||||||
{slotAltPrompt}
|
{slotAltPrompt}
|
||||||
</div>
|
</div>
|
||||||
<div className="field slot-decision-field">
|
{cancelField}
|
||||||
<input disabled={this.props.disabled} className="slot-decision" type="radio" name="slot-decision" value="decline" readyOnly={true} defaultChecked={this.props.slot_decision == 'decline'} /><label>{declineVerb} lesson
|
|
||||||
request</label>
|
|
||||||
</div>
|
|
||||||
{updateAllField}
|
{updateAllField}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -294,18 +294,24 @@ UserStore = context.UserStore
|
||||||
|
|
||||||
logger.debug("testDriveCount: " + testDriveCount)
|
logger.debug("testDriveCount: " + testDriveCount)
|
||||||
|
|
||||||
|
testDriveCountInt = parseInt(testDriveCount);
|
||||||
|
if context._.isNaN(testDriveCountInt)
|
||||||
|
testDriveCountInt = 3
|
||||||
|
|
||||||
|
context.JK.GA.trackTestDrivePurchase(testDriveCountInt);
|
||||||
|
|
||||||
if response.test_drive?.teacher_id
|
if response.test_drive?.teacher_id
|
||||||
teacher_id = response.test_drive.teacher_id
|
teacher_id = response.test_drive.teacher_id
|
||||||
if testDriveCount == 1
|
if testDriveCount == '1'
|
||||||
text = "You have purchased a TestDrive credit and have used it to request a JamClass with #{@state.teacher.name}. The teacher has received your request and should respond shortly."
|
text = "You have purchased 1 TestDrive credit and have used it to request a JamClass with #{@state.teacher.name}. The teacher has received your request and should respond shortly."
|
||||||
else
|
else
|
||||||
text = "You have purchased #{testDriveCount} TestDrive credits and have used 1 credit it to request a JamClass with #{@state.teacher.name}. The teacher has received your request and should respond shortly."
|
text = "You have purchased #{testDriveCount} TestDrive credits and have used 1 credit to request a JamClass with #{@state.teacher.name}. The teacher has received your request and should respond shortly."
|
||||||
location = "/client#/jamclass"
|
location = "/client#/jamclass"
|
||||||
else
|
else
|
||||||
if @state.teacher?.id
|
if @state.teacher?.id
|
||||||
|
|
||||||
# the user bought the testdrive, and there is a teacher of interest in context (but no booking)
|
# the user bought the testdrive, and there is a teacher of interest in context (but no booking)
|
||||||
if testDriveCount == 1
|
if testDriveCount == '1'
|
||||||
text = "You now have 1 TestDrive credit.<br/><br/>We've taken you to the lesson booking screen for the teacher you initially showed interest in."
|
text = "You now have 1 TestDrive credit.<br/><br/>We've taken you to the lesson booking screen for the teacher you initially showed interest in."
|
||||||
location = "/client#/jamclass/book-lesson/test-drive_" + teacher_id
|
location = "/client#/jamclass/book-lesson/test-drive_" + teacher_id
|
||||||
else
|
else
|
||||||
|
|
@ -313,7 +319,7 @@ UserStore = context.UserStore
|
||||||
location = "/client#/jamclass/book-lesson/test-drive_" + teacher_id
|
location = "/client#/jamclass/book-lesson/test-drive_" + teacher_id
|
||||||
else
|
else
|
||||||
# the user bought test drive, but 'cold' , i.e., no teacher in context
|
# the user bought test drive, but 'cold' , i.e., no teacher in context
|
||||||
if testDriveCount == 1
|
if testDriveCount == '1'
|
||||||
text = "You now have 1 TestDrive credit.<br/><br/>We've taken you to the Teacher Search screen, so you can search for teachers right for you."
|
text = "You now have 1 TestDrive credit.<br/><br/>We've taken you to the Teacher Search screen, so you can search for teachers right for you."
|
||||||
location = "/client#/teachers/search"
|
location = "/client#/teachers/search"
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
context = window
|
||||||
|
|
||||||
|
@MusicNotationUploadDialog = React.createClass({
|
||||||
|
|
||||||
|
mixins: [Reflux.listenTo(@AppStore, "onAppInit"), Reflux.listenTo(AttachmentStore, "onAttachmentStore")]
|
||||||
|
|
||||||
|
getInitialState: () ->
|
||||||
|
{
|
||||||
|
uploading: false
|
||||||
|
}
|
||||||
|
onAppInit: (@app) ->
|
||||||
|
|
||||||
|
onAttachmentStore: (attachmentState) ->
|
||||||
|
@setState(attachmentState)
|
||||||
|
|
||||||
|
handleCloseMessage: (e) ->
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
@app.layout.closeDialog('music-notation-upload-dialog')
|
||||||
|
|
||||||
|
render: () ->
|
||||||
|
|
||||||
|
if @state.uploading
|
||||||
|
state =
|
||||||
|
`<div>
|
||||||
|
<h3>Please wait while we upload your attachment to the lesson...</h3>
|
||||||
|
<div className="spinner spinner-large"></div>
|
||||||
|
</div>`
|
||||||
|
else
|
||||||
|
state =
|
||||||
|
`<div>
|
||||||
|
<h3>Your file has been uploaded.</h3>
|
||||||
|
</div>`
|
||||||
|
|
||||||
|
`<div className="MusicNotationUploadDialog">
|
||||||
|
<div className="content-head">
|
||||||
|
<img className="content-icon" src="/assets/content/icon_add.png" height={19} width={19}/>
|
||||||
|
|
||||||
|
<h1>Uploading Attachment</h1>
|
||||||
|
</div>
|
||||||
|
<div className="dialog-inner">
|
||||||
|
{state}
|
||||||
|
<div className="actions">
|
||||||
|
<a className="button-orange " onClick={this.handleCloseMessage}>CLOSE</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`
|
||||||
|
})
|
||||||
|
|
||||||
|
|
@ -704,14 +704,21 @@ proficiencyDescriptionMap = {
|
||||||
render: () ->
|
render: () ->
|
||||||
if @state.user?
|
if @state.user?
|
||||||
avatar = context.JK.resolveAvatarUrl(@state.user.photo_url);
|
avatar = context.JK.resolveAvatarUrl(@state.user.photo_url);
|
||||||
if @state.user?.teacher?
|
if @state.user?
|
||||||
mainContent = @mainContent()
|
if @state.user.teacher?
|
||||||
profileLeft = @profileLeft()
|
mainContent = @mainContent()
|
||||||
else
|
profileLeft = @profileLeft()
|
||||||
if context.JK.currentUserId? && @state.user?.id == context.JK.currentUserId
|
|
||||||
noTeacherProfile = `<div className="no-teacher-profile">You have no teacher profile defined yet. <a onClick={this.editTeacherProfile}>Start making one now.</a></div>`
|
|
||||||
else
|
else
|
||||||
noTeacherProfile = `<div className="no-teacher-profile">This user has no teacher profile.</div>`
|
if context.JK.currentUserId? && @state.user?.id == context.JK.currentUserId
|
||||||
|
noTeacherProfile = `<div className="no-teacher-profile">You have no teacher profile defined yet. <a onClick={this.editTeacherProfile}>Start making one now.</a></div>`
|
||||||
|
else
|
||||||
|
noTeacherProfile = `<div className="no-teacher-profile">This user has no teacher profile.</div>`
|
||||||
|
|
||||||
|
mainContent = `<div className="">
|
||||||
|
{noTeacherProfile}
|
||||||
|
</div>`
|
||||||
|
else
|
||||||
|
noTeacherProfile = `<div><div className="loading-profile">Loading profile...</div><div className="spinner spinner-large"></div></div>`
|
||||||
mainContent = `<div className="">
|
mainContent = `<div className="">
|
||||||
{noTeacherProfile}
|
{noTeacherProfile}
|
||||||
</div>`
|
</div>`
|
||||||
|
|
@ -730,10 +737,7 @@ proficiencyDescriptionMap = {
|
||||||
</div>`
|
</div>`
|
||||||
`<div className="content-body-scroller">
|
`<div className="content-body-scroller">
|
||||||
<div className="profile-header profile-head">
|
<div className="profile-header profile-head">
|
||||||
|
<JamClassSearchHeader teacher={this.state.user}/>
|
||||||
<div className="user-header">
|
|
||||||
<h2 id="username"></h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{actionButtons}
|
{actionButtons}
|
||||||
<br clear="all"/><br />
|
<br clear="all"/><br />
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,25 @@ LocationActions = @LocationActions
|
||||||
|
|
||||||
@TeacherSearchOptionsScreen = React.createClass({
|
@TeacherSearchOptionsScreen = React.createClass({
|
||||||
|
|
||||||
mixins: [Reflux.listenTo(@AppStore, "onAppInit"), Reflux.listenTo(@UserStore, "onUserChanged"),
|
mixins: [ICheckMixin, Reflux.listenTo(@AppStore, "onAppInit"), Reflux.listenTo(@UserStore, "onUserChanged"),
|
||||||
Reflux.listenTo(@TeacherSearchStore, "onTeacherSearchChanged")]
|
Reflux.listenTo(@TeacherSearchStore, "onTeacherSearchChanged")]
|
||||||
|
|
||||||
LIMIT: 20
|
LIMIT: 20
|
||||||
|
|
||||||
|
componentDidMount: () ->
|
||||||
|
@checkboxes = [
|
||||||
|
{selector: 'input.onlyMySchool', stateKey: 'onlyMySchool'},
|
||||||
|
{selector: 'input.beginner-level', stateKey: 'beginner-level'},
|
||||||
|
{selector: 'input.intermediate-level', stateKey: 'intermediate-level'},
|
||||||
|
{selector: 'input.advanced-level', stateKey: 'advanced-level'}]
|
||||||
|
|
||||||
|
@root = $(@getDOMNode())
|
||||||
|
|
||||||
|
@iCheckify()
|
||||||
|
|
||||||
|
componentDidUpdate: () ->
|
||||||
|
@iCheckify()
|
||||||
|
|
||||||
getInitialState: () ->
|
getInitialState: () ->
|
||||||
{options: {onlyMySchool: true}}
|
{options: {onlyMySchool: true}}
|
||||||
|
|
||||||
|
|
@ -71,6 +85,20 @@ LocationActions = @LocationActions
|
||||||
options.onlyMySchool = checked
|
options.onlyMySchool = checked
|
||||||
@setState(options)
|
@setState(options)
|
||||||
|
|
||||||
|
checkboxChanged: (e) ->
|
||||||
|
$target = $(e.target)
|
||||||
|
|
||||||
|
if $target.is('.onlyMySchool')
|
||||||
|
state = {}
|
||||||
|
state['onlyMySchool'] = $target.is(':checked')
|
||||||
|
@setState(state)
|
||||||
|
@onlyMySchoolChange(e)
|
||||||
|
else
|
||||||
|
state = {}
|
||||||
|
state[$target.attr('name')] = $target.is(':checked')
|
||||||
|
@setState(state)
|
||||||
|
@levelChanged(e)
|
||||||
|
|
||||||
render: () ->
|
render: () ->
|
||||||
if @state.user?.school_id?
|
if @state.user?.school_id?
|
||||||
onlySchoolOption =
|
onlySchoolOption =
|
||||||
|
|
@ -114,15 +142,15 @@ LocationActions = @LocationActions
|
||||||
<h3>Student Levels Taught:</h3>
|
<h3>Student Levels Taught:</h3>
|
||||||
|
|
||||||
<div className="teaching-level beginner-level">
|
<div className="teaching-level beginner-level">
|
||||||
<input name="beginner-level" type="checkbox" data-purpose="teaches_beginner" onChange={this.levelChanged} checked={this.state.options.teaches_beginner}></input>
|
<input className="teachlevel beginner-level" name="beginner-level" type="checkbox" data-purpose="teaches_beginner" onChange={this.levelChanged} checked={this.state.options.teaches_beginner}></input>
|
||||||
<label htmlFor="beginner-level">Beginner</label>
|
<label htmlFor="beginner-level">Beginner</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="teaching-level intermediate-level">
|
<div className="teaching-level intermediate-level">
|
||||||
<input name="intermediate-level" type="checkbox" data-purpose="teaches_intermediate" onChange={this.levelChanged} checked={this.state.options.teaches_intermediate}></input>
|
<input className="teachlevel intermediate-level" name="intermediate-level" type="checkbox" data-purpose="teaches_intermediate" onChange={this.levelChanged} checked={this.state.options.teaches_intermediate}></input>
|
||||||
<label htmlFor="intermediate-level">Intermediate</label>
|
<label htmlFor="intermediate-level">Intermediate</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="teaching-level advanced-level">
|
<div className="teaching-level advanced-level">
|
||||||
<input name="advanced-level" type="checkbox" data-purpose="teaches_advanced" onChange={this.levelChanged} checked={this.state.options.teaches_advanced}></input>
|
<input className="teachlevel advanced-level" name="advanced-level" type="checkbox" data-purpose="teaches_advanced" onChange={this.levelChanged} checked={this.state.options.teaches_advanced}></input>
|
||||||
<label htmlFor="advanced-level">Advanced</label>
|
<label htmlFor="advanced-level">Advanced</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ ProfileActions = @ProfileActions
|
||||||
refreshing: false
|
refreshing: false
|
||||||
|
|
||||||
getInitialState: () ->
|
getInitialState: () ->
|
||||||
{searchOptions: {}, results: [], user: null}
|
{searchOptions: {}, results: [], user: null, searching: false}
|
||||||
|
|
||||||
onAppInit: (@app) ->
|
onAppInit: (@app) ->
|
||||||
@app.bindScreen('teachers/search', {beforeShow: @beforeShow, afterShow: @afterShow, afterHide: @afterHide})
|
@app.bindScreen('teachers/search', {beforeShow: @beforeShow, afterShow: @afterShow, afterHide: @afterHide})
|
||||||
|
|
@ -58,7 +58,7 @@ ProfileActions = @ProfileActions
|
||||||
@needToSearch = true
|
@needToSearch = true
|
||||||
|
|
||||||
onTeacherSearchResultsStore: (results) ->
|
onTeacherSearchResultsStore: (results) ->
|
||||||
results.searching = false
|
#results.searching = false
|
||||||
@refreshing = false
|
@refreshing = false
|
||||||
@contentBodyScroller.find('.infinite-scroll-loader-2').remove()
|
@contentBodyScroller.find('.infinite-scroll-loader-2').remove()
|
||||||
@setState(results)
|
@setState(results)
|
||||||
|
|
@ -89,7 +89,7 @@ ProfileActions = @ProfileActions
|
||||||
if $scroller.scrollTop() + $scroller.innerHeight() + 100 >= $scroller[0].scrollHeight
|
if $scroller.scrollTop() + $scroller.innerHeight() + 100 >= $scroller[0].scrollHeight
|
||||||
$scroller.append('<div class="infinite-scroll-loader-2">... Loading more Teachers ...</div>')
|
$scroller.append('<div class="infinite-scroll-loader-2">... Loading more Teachers ...</div>')
|
||||||
@refreshing = true
|
@refreshing = true
|
||||||
@setState({searching: true})
|
#@setState({searching: true})
|
||||||
logger.debug("refreshing more teachers for infinite scroll")
|
logger.debug("refreshing more teachers for infinite scroll")
|
||||||
TeacherSearchResultsActions.nextPage()
|
TeacherSearchResultsActions.nextPage()
|
||||||
)
|
)
|
||||||
|
|
@ -149,7 +149,9 @@ ProfileActions = @ProfileActions
|
||||||
moreAboutTeacher: (user, e) ->
|
moreAboutTeacher: (user, e) ->
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
ProfileActions.viewTeacherProfile(user, '/client#/teachers/search', 'BACK TO TEACHER SEARCH')
|
context.location = "/client#/profile/teacher/#{user.id}"
|
||||||
|
|
||||||
|
#ProfileActions.viewTeacherProfile(user, '/client#/teachers/search', 'BACK TO TEACHER SEARCH')
|
||||||
|
|
||||||
bookTestDrive: (user, e) ->
|
bookTestDrive: (user, e) ->
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
@ -210,162 +212,60 @@ ProfileActions = @ProfileActions
|
||||||
target.trigger( 'destroy.dot' );
|
target.trigger( 'destroy.dot' );
|
||||||
teacherBio.css('height', 'auto')
|
teacherBio.css('height', 'auto')
|
||||||
|
|
||||||
createSearchDescription: () ->
|
|
||||||
searchOptions = TeacherSearchStore.getState()
|
|
||||||
|
|
||||||
summary = ''
|
|
||||||
|
|
||||||
if searchOptions.onlyMySchool
|
|
||||||
summary += "From My School Only"
|
|
||||||
|
|
||||||
instruments = searchOptions.instruments
|
|
||||||
if instruments? && instruments.length > 0
|
|
||||||
if instruments.length == 1
|
|
||||||
bit = "Instrument = #{InstrumentStore.display(instruments[0])}"
|
|
||||||
else
|
|
||||||
instruments.length > 1
|
|
||||||
bit = "Instruments = #{InstrumentStore.display(instruments[0])} ... "
|
|
||||||
if summary.length > 0
|
|
||||||
summary += ', '
|
|
||||||
summary += " #{bit}"
|
|
||||||
|
|
||||||
subjects = searchOptions.subjects
|
|
||||||
if subjects? && subjects.length > 0
|
|
||||||
if subjects.length == 1
|
|
||||||
bit = "Subject = #{SubjectStore.display(subjects[0])}"
|
|
||||||
else
|
|
||||||
subjects.length > 1
|
|
||||||
bit = "Subjects = #{SubjectStore.display(subjects[0])} ... "
|
|
||||||
if summary.length > 0
|
|
||||||
summary += ', '
|
|
||||||
summary += " #{bit}"
|
|
||||||
|
|
||||||
genres = searchOptions.genres
|
|
||||||
if genres? && genres.length > 0
|
|
||||||
if genres.length == 1
|
|
||||||
bit = "Genre = #{GenreStore.display(genres[0])}"
|
|
||||||
else
|
|
||||||
genres.length > 1
|
|
||||||
bit = "Genres = #{GenreStore.display(genres[0])} ... "
|
|
||||||
if summary.length > 0
|
|
||||||
summary += ', '
|
|
||||||
summary += " #{bit}"
|
|
||||||
|
|
||||||
languages = searchOptions.languages
|
|
||||||
if languages? && languages.length > 0
|
|
||||||
if languages.length == 1
|
|
||||||
bit = "Language = #{LanguageStore.display(languages[0])}"
|
|
||||||
else
|
|
||||||
languages.length > 1
|
|
||||||
bit = "Languages = #{LanguageStore.display(languages[0])} ... "
|
|
||||||
if summary.length > 0
|
|
||||||
summary += ', '
|
|
||||||
summary += " #{bit}"
|
|
||||||
|
|
||||||
if searchOptions.teaches_beginner || searchOptions.teaches_intermediate || searchOptions.teaches_advanced
|
|
||||||
bit = "Teaches "
|
|
||||||
qualifier = ''
|
|
||||||
if searchOptions.teaches_beginner
|
|
||||||
qualifier += "Beginner"
|
|
||||||
if searchOptions.teaches_intermediate
|
|
||||||
if qualifier.length > 0
|
|
||||||
qualifier += ", "
|
|
||||||
qualifier += "Intermediate"
|
|
||||||
if searchOptions.teaches_advanced
|
|
||||||
if qualifier.length > 0
|
|
||||||
qualifier += ", "
|
|
||||||
qualifier += "Advanced"
|
|
||||||
if summary.length > 0
|
|
||||||
summary += ', '
|
|
||||||
summary += " #{bit}#{qualifier}"
|
|
||||||
|
|
||||||
if searchOptions.student_age?
|
|
||||||
if summary.length > 0
|
|
||||||
summary += ', '
|
|
||||||
summary += "Student Age = #{searchOptions.student_age}"
|
|
||||||
|
|
||||||
if searchOptions.years_teaching?
|
|
||||||
if summary.length > 0
|
|
||||||
summary += ', '
|
|
||||||
summary += "Years Teaching = #{searchOptions.years_teaching}"
|
|
||||||
|
|
||||||
if searchOptions.location?.country?
|
|
||||||
if summary.length > 0
|
|
||||||
summary += ', '
|
|
||||||
summary += "Country = #{searchOptions.location.country}"
|
|
||||||
|
|
||||||
if searchOptions.location?.region?
|
|
||||||
|
|
||||||
if summary.length > 0
|
|
||||||
summary += ', '
|
|
||||||
summary += "Region = #{searchOptions.location.region}"
|
|
||||||
|
|
||||||
|
|
||||||
if summary.length == 0
|
|
||||||
summary = 'all teachers'
|
|
||||||
|
|
||||||
summary
|
|
||||||
|
|
||||||
render: () ->
|
render: () ->
|
||||||
|
|
||||||
searchDesc = @createSearchDescription()
|
|
||||||
|
|
||||||
resultsJsx = []
|
resultsJsx = []
|
||||||
|
|
||||||
for user in @state.results
|
if @state.currentPage == 1 && @state.searching
|
||||||
|
resultsJsx = `<div className="spinner spinner-large"></div>`
|
||||||
|
else
|
||||||
|
for user in @state.results
|
||||||
|
|
||||||
photo_url = user.photo_url
|
photo_url = user.photo_url
|
||||||
if !photo_url?
|
if !photo_url?
|
||||||
photo_url = '/assets/shared/avatar_generic.png'
|
photo_url = '/assets/shared/avatar_generic.png'
|
||||||
|
|
||||||
bio = user.teacher.biography
|
bio = user.teacher.biography
|
||||||
if !bio?
|
if !bio?
|
||||||
bio = 'No bio'
|
bio = 'No bio'
|
||||||
|
|
||||||
school_on_school = user.teacher.school_id? && @state.user?.school_id? && user.teacher.school_id == @state.user.school_id
|
school_on_school = user.teacher.school_id? && @state.user?.school_id? && user.teacher.school_id == @state.user.school_id
|
||||||
|
|
||||||
bookSingleBtn = null
|
bookSingleBtn = null
|
||||||
bookTestDriveBtn = null
|
bookTestDriveBtn = null
|
||||||
|
|
||||||
if !school_on_school && (!@state.user? || @state.user.remaining_test_drives > 0 || @state.user['can_buy_test_drive?'])
|
if !school_on_school && (!@state.user? || @state.user.remaining_test_drives > 0 || @state.user['can_buy_test_drive?'])
|
||||||
bookTestDriveBtn = `<a className="button-orange try-test-drive" onClick={this.bookTestDrive.bind(this, user)}>BOOK TESTDRIVE LESSON</a>`
|
bookTestDriveBtn = `<a className="button-orange try-test-drive" onClick={this.bookTestDrive.bind(this, user)}>BOOK TESTDRIVE LESSON</a>`
|
||||||
else
|
else
|
||||||
bookSingleBtn = `<a className="button-orange try-normal" onClick={this.bookNormalLesson.bind(this, user)}>BOOK LESSON</a>`
|
bookSingleBtn = `<a className="button-orange try-normal" onClick={this.bookNormalLesson.bind(this, user)}>BOOK LESSON</a>`
|
||||||
resultsJsx.push(`<div key={user.id} className="teacher-search-result" data-teacher-id={user.id}>
|
resultsJsx.push(`<div key={user.id} className="teacher-search-result" data-teacher-id={user.id}>
|
||||||
<div className="user-avatar">
|
<div className="user-avatar">
|
||||||
<div className="avatar small">
|
<div className="avatar small">
|
||||||
<img src={photo_url} />
|
<img src={photo_url} />
|
||||||
|
</div>
|
||||||
|
<div className="user-name">
|
||||||
|
{user.name}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="user-name">
|
<div className="user-info">
|
||||||
{user.name}
|
<div className="teacher-bio">
|
||||||
|
{bio}
|
||||||
|
<a className="readmore" onClick={this.readMore}>more</a>
|
||||||
|
</div>
|
||||||
|
<div className="teacher-actions">
|
||||||
|
<a className="button-orange more-about-teacher" onClick={this.moreAboutTeacher.bind(this, user)}>MORE ABOUT THIS TEACHER</a>
|
||||||
|
{bookTestDriveBtn}
|
||||||
|
{bookSingleBtn}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<br className="clearall" />
|
||||||
<div className="user-info">
|
</div>`)
|
||||||
<div className="teacher-bio">
|
|
||||||
{bio}
|
|
||||||
<a className="readmore" onClick={this.readMore}>more</a>
|
|
||||||
</div>
|
|
||||||
<div className="teacher-actions">
|
|
||||||
<a className="button-orange more-about-teacher" onClick={this.moreAboutTeacher.bind(this, user)}>MORE ABOUT THIS TEACHER</a>
|
|
||||||
{bookTestDriveBtn}
|
|
||||||
{bookSingleBtn}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<br className="clearall" />
|
|
||||||
</div>`)
|
|
||||||
|
|
||||||
`<div className="content-body-scroller">
|
`<div className="content-body-scroller">
|
||||||
<div className="screen-content">
|
<div className="screen-content">
|
||||||
<div className="header">
|
<div className="header">
|
||||||
<a href="/client#/home">JamKazam Home</a> :
|
<JamClassSearchHeader/>
|
||||||
<a href="/client#/jamclass">JamClass Home</a> :
|
|
||||||
<a className="teacher-search-options" href="/client#/jamclass/searchOptions" >Teachers Search</a><span className="teacher-quote"> :</span>
|
|
||||||
<span className="search-results-options">
|
|
||||||
<span className="search-description">
|
|
||||||
<span className="results-text">Search Results / </span>
|
|
||||||
<span className="search-summary">{searchDesc}</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="results">
|
<div className="results">
|
||||||
{resultsJsx}
|
{resultsJsx}
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,5 @@ context = window
|
||||||
startAttachNotation: {}
|
startAttachNotation: {}
|
||||||
startAttachAudio: {}
|
startAttachAudio: {}
|
||||||
uploadNotations: {}
|
uploadNotations: {}
|
||||||
|
uploadAudios: {}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -45,8 +45,8 @@ rest = context.JK.Rest()
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
<p>While you're getting this done, if you want to learn more about all the nifty features you can access in
|
<p>While you're getting this done, if you want to learn more about all the nifty features you can access in
|
||||||
JamClass and in JamKazam in general, you can check out our online <a href="" target="_blank"
|
JamClass and in JamKazam in general, you can check out our online <a target="_blank"
|
||||||
onClick={alert.bind('not yet')}>JamClass
|
href="https://jamkazam.desk.com/customer/en/portal/topics/926073-jamclass-online-music-lessons---for-students/articles">JamClass
|
||||||
User Guide</a>.</p>
|
User Guide</a>.</p>
|
||||||
</div>`
|
</div>`
|
||||||
`<div className="top-container">
|
`<div className="top-container">
|
||||||
|
|
|
||||||
|
|
@ -40,14 +40,18 @@ teacherActions = window.JK.Actions.Teacher
|
||||||
enableICheck: (e) ->
|
enableICheck: (e) ->
|
||||||
if !@root?
|
if !@root?
|
||||||
return
|
return
|
||||||
checkBoxes = @root.find('input[type="checkbox"]')
|
|
||||||
if checkBoxes.length > 0
|
for checkbox in @checkboxes
|
||||||
context.JK.checkbox(checkBoxes)
|
selector = checkbox.selector
|
||||||
checkBoxes.on('ifChanged', (e) => @checkIfCanFire(e))
|
|
||||||
radioBoxes = @root.find('input[type="radio"]')
|
checkBoxes = @root.find(selector + '[type="checkbox"]')
|
||||||
if radioBoxes.length > 0
|
if checkBoxes.length > 0
|
||||||
context.JK.checkbox(radioBoxes)
|
context.JK.checkbox(checkBoxes)
|
||||||
radioBoxes.on('ifChanged', (e) => @checkIfCanFire(e))
|
checkBoxes.on('ifChanged', (e) => @checkIfCanFire(e))
|
||||||
|
radioBoxes = @root.find(selector + '[type="radio"]')
|
||||||
|
if radioBoxes.length > 0
|
||||||
|
context.JK.checkbox(radioBoxes)
|
||||||
|
radioBoxes.on('ifChanged', (e) => @checkIfCanFire(e))
|
||||||
|
|
||||||
true
|
true
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ teacherActions = window.JK.Actions.Teacher
|
||||||
lesson.isAdmin = context.JK.currentUserAdmin
|
lesson.isAdmin = context.JK.currentUserAdmin
|
||||||
lesson.schoolOnSchool = lesson['school_on_school?']
|
lesson.schoolOnSchool = lesson['school_on_school?']
|
||||||
lesson.cardNotOk = !lesson.schoolOnSchool && !lesson.lesson_booking.card_presumed_ok
|
lesson.cardNotOk = !lesson.schoolOnSchool && !lesson.lesson_booking.card_presumed_ok
|
||||||
|
lesson.isActive = lesson['is_active?']
|
||||||
if (lesson.status == 'requested' || lesson.status == 'countered')
|
if (lesson.status == 'requested' || lesson.status == 'countered')
|
||||||
lesson.isRequested = true
|
lesson.isRequested = true
|
||||||
if lesson.cardNotOk
|
if lesson.cardNotOk
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,8 @@ AttachmentActions = @AttachmentActions
|
||||||
formData.append('lesson_session_id', @lessonId);
|
formData.append('lesson_session_id', @lessonId);
|
||||||
formData.append('attachment_type', 'notation')
|
formData.append('attachment_type', 'notation')
|
||||||
|
|
||||||
|
@app.layout.showDialog('music-notation-upload-dialog')
|
||||||
|
|
||||||
rest.uploadMusicNotations(formData)
|
rest.uploadMusicNotations(formData)
|
||||||
.done((response) => @doneUploadingNotatations(notations, response, doneCallback, failCallback))
|
.done((response) => @doneUploadingNotatations(notations, response, doneCallback, failCallback))
|
||||||
.fail((jqXHR) => @failUploadingNotations(jqXHR, failCallback))
|
.fail((jqXHR) => @failUploadingNotations(jqXHR, failCallback))
|
||||||
|
|
@ -137,6 +139,71 @@ AttachmentActions = @AttachmentActions
|
||||||
else
|
else
|
||||||
@app.notifyServerError(jqXHR, "Unable to upload music notations");
|
@app.notifyServerError(jqXHR, "Unable to upload music notations");
|
||||||
|
|
||||||
|
onUploadAudios: (notations, doneCallback, failCallback) ->
|
||||||
|
logger.debug("beginning upload of audio", notations)
|
||||||
|
@uploading = true
|
||||||
|
@changed()
|
||||||
|
|
||||||
|
formData = new FormData()
|
||||||
|
maxExceeded = false;
|
||||||
|
$.each(notations, (i, file) => (
|
||||||
|
max = 10 * 1024 * 1024;
|
||||||
|
if file.size > max
|
||||||
|
maxExceeded = true
|
||||||
|
return false
|
||||||
|
|
||||||
|
formData.append('files[]', file)
|
||||||
|
))
|
||||||
|
|
||||||
|
if maxExceeded
|
||||||
|
@app.notify({
|
||||||
|
title: "Maximum Music Audio Size Exceeded",
|
||||||
|
text: "You can only upload files up to 10 megabytes in size."
|
||||||
|
})
|
||||||
|
failCallback()
|
||||||
|
@uploading = false
|
||||||
|
@changed()
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
formData.append('lesson_session_id', @lessonId);
|
||||||
|
formData.append('attachment_type', 'audio')
|
||||||
|
|
||||||
|
@app.layout.showDialog('music-notation-upload-dialog')
|
||||||
|
|
||||||
|
rest.uploadMusicNotations(formData)
|
||||||
|
.done((response) => @doneUploadingAudios(notations, response, doneCallback, failCallback))
|
||||||
|
.fail((jqXHR) => @failUploadingAudios(jqXHR, failCallback))
|
||||||
|
|
||||||
|
doneUploadingAudios: (notations, response, doneCallback, failCallback) ->
|
||||||
|
@uploading = false
|
||||||
|
@changed()
|
||||||
|
error_files = [];
|
||||||
|
$.each(response, (i, music_notation) => (
|
||||||
|
if music_notation.errors
|
||||||
|
error_files.push(notations[i].name)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if error_files.length > 0
|
||||||
|
failCallback()
|
||||||
|
@app.notifyAlert("Failed to upload audio files.", error_files.join(', '));
|
||||||
|
else
|
||||||
|
doneCallback()
|
||||||
|
|
||||||
|
failUploadingAudios: (jqXHR, failCallback) ->
|
||||||
|
@uploading = false
|
||||||
|
@changed()
|
||||||
|
if jqXHR.status == 413
|
||||||
|
# the file is too big. Let the user know.
|
||||||
|
# This should happen when they select the file, but a misconfiguration on the server could cause this.
|
||||||
|
@app.notify({
|
||||||
|
title: "Maximum Music Audio Size Exceeded",
|
||||||
|
text: "You can only upload files up to 10 megabytes in size."
|
||||||
|
})
|
||||||
|
else
|
||||||
|
@app.notifyServerError(jqXHR, "Unable to upload music audio files");
|
||||||
|
|
||||||
|
|
||||||
changed: () ->
|
changed: () ->
|
||||||
this.trigger({lessonId: @lessonId, uploading: @uploading})
|
this.trigger({lessonId: @lessonId, uploading: @uploading})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ TeacherSearchResultsActions = @TeacherSearchResultsActions
|
||||||
results: []
|
results: []
|
||||||
page: 1
|
page: 1
|
||||||
limit: 20
|
limit: 20
|
||||||
|
searching: false
|
||||||
|
|
||||||
init: ->
|
init: ->
|
||||||
# Register with the app store to get @app
|
# Register with the app store to get @app
|
||||||
|
|
@ -22,6 +23,7 @@ TeacherSearchResultsActions = @TeacherSearchResultsActions
|
||||||
onReset: () ->
|
onReset: () ->
|
||||||
@results = []
|
@results = []
|
||||||
@page = 1
|
@page = 1
|
||||||
|
@searching = true
|
||||||
@changed()
|
@changed()
|
||||||
|
|
||||||
query = @createQuery()
|
query = @createQuery()
|
||||||
|
|
@ -29,11 +31,13 @@ TeacherSearchResultsActions = @TeacherSearchResultsActions
|
||||||
rest.searchTeachers(query)
|
rest.searchTeachers(query)
|
||||||
.done((response) =>
|
.done((response) =>
|
||||||
@next = response.next
|
@next = response.next
|
||||||
|
@searching = false
|
||||||
@results.push.apply(@results, response.entries)
|
@results.push.apply(@results, response.entries)
|
||||||
@changed()
|
@changed()
|
||||||
)
|
)
|
||||||
.fail((jqXHR, textStatus, errorMessage) =>
|
.fail((jqXHR, textStatus, errorMessage) =>
|
||||||
|
@searching = false
|
||||||
|
@changed()
|
||||||
@app.ajaxError(jqXHR, textStatus, errorMessage)
|
@app.ajaxError(jqXHR, textStatus, errorMessage)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -42,18 +46,22 @@ TeacherSearchResultsActions = @TeacherSearchResultsActions
|
||||||
|
|
||||||
query = @createQuery()
|
query = @createQuery()
|
||||||
|
|
||||||
|
@searching = true
|
||||||
rest.searchTeachers(query)
|
rest.searchTeachers(query)
|
||||||
.done((response) =>
|
.done((response) =>
|
||||||
@next = response.next
|
@next = response.next
|
||||||
@results.push.apply(@results, response.entries)
|
@results.push.apply(@results, response.entries)
|
||||||
|
@searching = false
|
||||||
@changed()
|
@changed()
|
||||||
)
|
)
|
||||||
.fail((jqXHR, textStatus, errorMessage) =>
|
.fail((jqXHR, textStatus, errorMessage) =>
|
||||||
|
@searching = false
|
||||||
@app.ajaxError(jqXHR, textStatus, errorMessage)
|
@app.ajaxError(jqXHR, textStatus, errorMessage)
|
||||||
|
@changed()
|
||||||
)
|
)
|
||||||
|
|
||||||
getState: () ->
|
getState: () ->
|
||||||
({results: @results, next: @next, currentPage: @page})
|
({results: @results, next: @next, currentPage: @page, searching: @searching})
|
||||||
|
|
||||||
changed:() ->
|
changed:() ->
|
||||||
@trigger(@getState())
|
@trigger(@getState())
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,8 @@
|
||||||
|
|
||||||
$('.like-link').click(function() {
|
$('.like-link').click(function() {
|
||||||
var like_site = $(this).data('site');
|
var like_site = $(this).data('site');
|
||||||
JK.GA.trackJKSocial(JK.GA.Categories.jkLike, like_site, JK.clientType());
|
// removed because we are juggling 20 events max in GA
|
||||||
|
//JK.GA.trackJKSocial(JK.GA.Categories.jkLike, like_site, JK.clientType());
|
||||||
window.open("/endorse/0/"+like_site, '_blank');
|
window.open("/endorse/0/"+like_site, '_blank');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -190,7 +190,7 @@
|
||||||
|
|
||||||
logger.debug("marking all unassigned inputs length=(" + $allInputs.length + ")")
|
logger.debug("marking all unassigned inputs length=(" + $allInputs.length + ")")
|
||||||
|
|
||||||
var maxTries = 20;
|
var maxTries = 12; // we only allow up to 6 tracks, 12 inputs
|
||||||
|
|
||||||
for(var i = 0; i < maxTries; i++) {
|
for(var i = 0; i < maxTries; i++) {
|
||||||
$unassignedInputs = $inputChannels.find('input[type="checkbox"]:not(:checked)');
|
$unassignedInputs = $inputChannels.find('input[type="checkbox"]:not(:checked)');
|
||||||
|
|
@ -1105,6 +1105,11 @@
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!passedOnce) {
|
||||||
|
passedOnce = true;
|
||||||
|
autoAssignToSingleInput();
|
||||||
|
}
|
||||||
|
|
||||||
if(!savedProfile) {
|
if(!savedProfile) {
|
||||||
context.jamClient.FTUESetMusicProfileName(gearUtils.createProfileName(selectedDeviceInfo));
|
context.jamClient.FTUESetMusicProfileName(gearUtils.createProfileName(selectedDeviceInfo));
|
||||||
var result = context.jamClient.FTUESave(true);
|
var result = context.jamClient.FTUESave(true);
|
||||||
|
|
@ -1122,10 +1127,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!passedOnce) {
|
|
||||||
passedOnce = true;
|
|
||||||
autoAssignToSingleInput();
|
|
||||||
}
|
|
||||||
// keep the shared state between step 2 and step 3 up-to-date
|
// keep the shared state between step 2 and step 3 up-to-date
|
||||||
wizard.setChosenInputs(context._.map($assignedInputs, function(input) { return $(input).attr('data-id') }));
|
wizard.setChosenInputs(context._.map($assignedInputs, function(input) { return $(input).attr('data-id') }));
|
||||||
|
|
||||||
|
|
@ -1147,12 +1148,12 @@
|
||||||
}
|
}
|
||||||
var $unassignedInputs = $inputChannels.find('input[type="checkbox"]:not(:checked)')
|
var $unassignedInputs = $inputChannels.find('input[type="checkbox"]:not(:checked)')
|
||||||
$allInputs.eq(0).iCheck('check').attr('checked', 'checked')
|
$allInputs.eq(0).iCheck('check').attr('checked', 'checked')
|
||||||
var firstInputDomNode = $allInputs.get(0)
|
|
||||||
|
|
||||||
var maxTries = 20;
|
var maxTries = 36;
|
||||||
for(var i = 0; i < maxTries; i++) {
|
for(var i = 0; i < maxTries; i++) {
|
||||||
var $assignedInputs = $inputChannels.find('input[type="checkbox"]:checked')
|
var $assignedInputs = $inputChannels.find('input[type="checkbox"]:checked')
|
||||||
|
var $allInputs = $inputChannels.find('input[type="checkbox"]')
|
||||||
|
var firstInputDomNode = $allInputs.get(0)
|
||||||
if ($assignedInputs.length == 1) {
|
if ($assignedInputs.length == 1) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -1169,9 +1170,7 @@
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onFocus() {
|
function onFocus() {
|
||||||
|
|
@ -1182,6 +1181,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function newSession() {
|
function newSession() {
|
||||||
|
passedOnce = false;
|
||||||
savedProfile = false;
|
savedProfile = false;
|
||||||
initialScan = false;
|
initialScan = false;
|
||||||
deviceInformation = gearUtils.loadDeviceInfo();
|
deviceInformation = gearUtils.loadDeviceInfo();
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,15 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.onlyOption {
|
||||||
|
input[type="radio"], .iradio_minimal {
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
.slot-alt-prompt {
|
||||||
|
padding-left:0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.slot-detail {
|
.slot-detail {
|
||||||
color: $ColorTextTypical;
|
color: $ColorTextTypical;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
@ -160,6 +169,14 @@
|
||||||
.user-name {
|
.user-name {
|
||||||
line-height:48px;
|
line-height:48px;
|
||||||
vertical-align:middle;
|
vertical-align:middle;
|
||||||
|
color:white;
|
||||||
|
}
|
||||||
|
.request-sent {
|
||||||
|
color:$ColorTextTypical;
|
||||||
|
|
||||||
|
.generic-time-stmt, .proposing-new-time {
|
||||||
|
margin-top:10px !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.avatar {
|
.avatar {
|
||||||
position:absolute;
|
position:absolute;
|
||||||
|
|
|
||||||
|
|
@ -194,7 +194,22 @@
|
||||||
.years {float:right}
|
.years {float:right}
|
||||||
}
|
}
|
||||||
.profileNavActions {
|
.profileNavActions {
|
||||||
margin-right: -3px;
|
right:20px;
|
||||||
|
top:10px;
|
||||||
|
position:absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.spinner-large {
|
||||||
|
width:200px;
|
||||||
|
height:200px;
|
||||||
|
position:relative;
|
||||||
|
margin:0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-profile {
|
||||||
|
text-align:center;
|
||||||
|
color:white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ratings-block {
|
.ratings-block {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,41 @@
|
||||||
@import "client/common";
|
@import "client/common";
|
||||||
|
|
||||||
|
|
||||||
|
.jamclass-search-header {
|
||||||
|
|
||||||
|
|
||||||
|
a {
|
||||||
|
font-size:16px;
|
||||||
|
text-decoration:underline;
|
||||||
|
margin-bottom:5px;
|
||||||
|
}
|
||||||
|
.search-results-options {
|
||||||
|
font-size:16px;
|
||||||
|
color:$ColorTextTypical;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-summary {
|
||||||
|
font-size:11px;
|
||||||
|
}
|
||||||
|
.search-description {
|
||||||
|
white-space: nowrap;
|
||||||
|
max-width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
vertical-align:middle;
|
||||||
|
}
|
||||||
|
.search-summary {
|
||||||
|
line-height:16px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
.results-text {
|
||||||
|
&.link {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#teacherSearch {
|
#teacherSearch {
|
||||||
div[data-react-class="TeacherSearchScreen"] {
|
div[data-react-class="TeacherSearchScreen"] {
|
||||||
height:100%;
|
height:100%;
|
||||||
|
|
@ -9,29 +45,21 @@
|
||||||
padding:20px;
|
padding:20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.jamclass-search-header {
|
||||||
margin-bottom:10px;
|
margin-bottom:10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner-large {
|
||||||
|
width:200px;
|
||||||
a {
|
height:200px;
|
||||||
font-size:16px;
|
position:relative;
|
||||||
text-decoration:underline;
|
margin:0 auto;
|
||||||
margin-bottom:5px;
|
|
||||||
}
|
|
||||||
.search-results-options {
|
|
||||||
font-size:16px;
|
|
||||||
color:$ColorTextTypical;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
a.readmore {
|
a.readmore {
|
||||||
display:none;
|
display:none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-summary {
|
|
||||||
font-size:11px;
|
|
||||||
}
|
|
||||||
.teacher-search-result {
|
.teacher-search-result {
|
||||||
@include border_box_sizing;
|
@include border_box_sizing;
|
||||||
clear:both;
|
clear:both;
|
||||||
|
|
@ -108,17 +136,7 @@
|
||||||
padding-right: 31px;
|
padding-right: 31px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
.search-description {
|
|
||||||
white-space: nowrap;
|
|
||||||
max-width: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
vertical-align:middle;
|
|
||||||
}
|
|
||||||
.search-summary {
|
|
||||||
line-height:16px;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
.result-text {
|
.result-text {
|
||||||
line-height:16px;
|
line-height:16px;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,29 @@
|
||||||
.checkbox-scroller {
|
.checkbox-scroller {
|
||||||
height:100px;
|
height:100px;
|
||||||
margin-top:0;
|
margin-top:0;
|
||||||
|
.checkItem {
|
||||||
|
clear: both;
|
||||||
|
margin-bottom:4px;
|
||||||
|
label {
|
||||||
|
color: black;
|
||||||
|
display: inline;
|
||||||
|
float: left;
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
.icheckbox_minimal {
|
||||||
|
color: black;
|
||||||
|
display: inline;
|
||||||
|
float: left;
|
||||||
|
font-size: 1em;
|
||||||
|
margin-right:5px;
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
width: auto;
|
||||||
|
text-align: left;
|
||||||
|
float: left;
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
min-width:200px;
|
min-width:200px;
|
||||||
width:25%;
|
width:25%;
|
||||||
|
|
@ -72,6 +95,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.student-levels-taught {
|
.student-levels-taught {
|
||||||
|
.icheckbox_minimal {
|
||||||
|
top:3px;
|
||||||
|
margin-right:5px;
|
||||||
|
}
|
||||||
.teaching-level {
|
.teaching-level {
|
||||||
margin-bottom:10px;
|
margin-bottom:10px;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
@import "client/common";
|
||||||
|
|
||||||
|
#music-notation-upload-dialog {
|
||||||
|
width: 500px;
|
||||||
|
max-height:500px;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
color:white;
|
||||||
|
margin-bottom:20px;
|
||||||
|
}
|
||||||
|
.dialog-inner {
|
||||||
|
width: auto;
|
||||||
|
height:100%;
|
||||||
|
@include border_box_sizing;
|
||||||
|
margin-top: -29px;
|
||||||
|
padding: 50px 25px 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div[data-react-class="MusicNotationUploadDialog"] {
|
||||||
|
|
||||||
|
}
|
||||||
|
.MusicNotationUploadDialog {
|
||||||
|
height:100%;
|
||||||
|
}
|
||||||
|
.spinner-large {
|
||||||
|
width:200px;
|
||||||
|
height:200px;
|
||||||
|
line-height: 200px;
|
||||||
|
position:relative;
|
||||||
|
margin:25px auto;
|
||||||
|
}
|
||||||
|
.actions {
|
||||||
|
text-align:right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -16,6 +16,11 @@
|
||||||
overflow-y:auto;
|
overflow-y:auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.title-artist {
|
||||||
|
margin-left:25px;
|
||||||
|
}
|
||||||
|
|
||||||
.action-buttons {
|
.action-buttons {
|
||||||
margin-bottom:10px;
|
margin-bottom:10px;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@
|
||||||
height: 36px;
|
height: 36px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.recording-controls {
|
.recording-controls {
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
padding: 3px 5px 3px 10px;
|
padding: 3px 5px 3px 10px;
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ class ApiLessonBookingsController < ApiController
|
||||||
specified_slot.timezone = params[:timezone]
|
specified_slot.timezone = params[:timezone]
|
||||||
slots << specified_slot
|
slots << specified_slot
|
||||||
end
|
end
|
||||||
|
|
||||||
@lesson_booking = LessonBooking.book_free(current_user, teacher, slots, params[:description])
|
@lesson_booking = LessonBooking.book_free(current_user, teacher, slots, params[:description])
|
||||||
|
|
||||||
if @lesson_booking.errors.any?
|
if @lesson_booking.errors.any?
|
||||||
|
|
@ -129,10 +129,10 @@ class ApiLessonBookingsController < ApiController
|
||||||
def accept
|
def accept
|
||||||
next_lesson = @lesson_booking.next_lesson
|
next_lesson = @lesson_booking.next_lesson
|
||||||
result = next_lesson.accept({
|
result = next_lesson.accept({
|
||||||
message: params[:message],
|
message: params[:message],
|
||||||
slot: params[:slot],
|
slot: params[:slot],
|
||||||
accepter: current_user
|
accepter: current_user
|
||||||
})
|
})
|
||||||
|
|
||||||
if result.errors.any?
|
if result.errors.any?
|
||||||
if result.is_a?(JamRuby::LessonBooking)
|
if result.is_a?(JamRuby::LessonBooking)
|
||||||
|
|
@ -159,16 +159,20 @@ class ApiLessonBookingsController < ApiController
|
||||||
|
|
||||||
slot = LessonBookingSlot.new
|
slot = LessonBookingSlot.new
|
||||||
if @lesson_booking.recurring
|
if @lesson_booking.recurring
|
||||||
slot.slot_type = LessonBookingSlot::SLOT_TYPE_RECURRING
|
if params[:update_all]
|
||||||
slot.day_of_week = params[:day_of_week]
|
slot.slot_type = LessonBookingSlot::SLOT_TYPE_RECURRING
|
||||||
|
else
|
||||||
|
slot.slot_type = LessonBookingSlot::SLOT_TYPE_SINGLE
|
||||||
|
end
|
||||||
else
|
else
|
||||||
slot.slot_type = LessonBookingSlot::SLOT_TYPE_SINGLE
|
slot.slot_type = LessonBookingSlot::SLOT_TYPE_SINGLE
|
||||||
|
end
|
||||||
|
|
||||||
if params[:date].present?
|
slot.day_of_week = params[:day_of_week]
|
||||||
day = params[:date]
|
if params[:date].present?
|
||||||
day = Date.parse(day) if day && !day.include?('NaN')
|
day = params[:date]
|
||||||
slot.preferred_day = day
|
day = Date.parse(day) if day && !day.include?('NaN')
|
||||||
end
|
slot.preferred_day = day
|
||||||
end
|
end
|
||||||
slot.hour = params[:hour]
|
slot.hour = params[:hour]
|
||||||
slot.minute = params[:minute]
|
slot.minute = params[:minute]
|
||||||
|
|
@ -176,10 +180,10 @@ class ApiLessonBookingsController < ApiController
|
||||||
slot.update_all = params[:update_all]
|
slot.update_all = params[:update_all]
|
||||||
|
|
||||||
result = target_lesson.counter({
|
result = target_lesson.counter({
|
||||||
proposer: current_user,
|
proposer: current_user,
|
||||||
message: params[:message],
|
message: params[:message],
|
||||||
slot: slot
|
slot: slot
|
||||||
})
|
})
|
||||||
|
|
||||||
if result.errors.any?
|
if result.errors.any?
|
||||||
if result.is_a?(JamRuby::LessonBooking)
|
if result.is_a?(JamRuby::LessonBooking)
|
||||||
|
|
@ -188,7 +192,7 @@ class ApiLessonBookingsController < ApiController
|
||||||
recursive_errors(result, [:lesson_booking_slots])
|
recursive_errors(result, [:lesson_booking_slots])
|
||||||
else
|
else
|
||||||
raise "unknown response type in counter #{result.class}"
|
raise "unknown response type in counter #{result.class}"
|
||||||
end
|
end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
@lesson_booking.reload
|
@lesson_booking.reload
|
||||||
|
|
@ -203,10 +207,10 @@ class ApiLessonBookingsController < ApiController
|
||||||
@lesson_session = target_lesson
|
@lesson_session = target_lesson
|
||||||
|
|
||||||
result = target_lesson.cancel({
|
result = target_lesson.cancel({
|
||||||
canceler: current_user,
|
canceler: current_user,
|
||||||
message: params[:message],
|
message: params[:message],
|
||||||
update_all: true
|
update_all: params[:update_all]
|
||||||
})
|
})
|
||||||
|
|
||||||
if result.errors.any?
|
if result.errors.any?
|
||||||
if result.is_a?(JamRuby::LessonBooking)
|
if result.is_a?(JamRuby::LessonBooking)
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ class ApiLessonSessionsController < ApiController
|
||||||
|
|
||||||
if params[:update_all]
|
if params[:update_all]
|
||||||
# check if the next scheduled lesson is doable
|
# check if the next scheduled lesson is doable
|
||||||
if 24.hours.from_now > @lesson_session.booking.next_lesson.music_session.scheduled_start
|
if 24.hours.from_now > @lesson_session.lesson_booking.next_lesson.music_session.scheduled_start
|
||||||
response = {message: 'time_limit'}
|
response = {message: 'time_limit'}
|
||||||
render :json => response, :status => 422
|
render :json => response, :status => 422
|
||||||
return
|
return
|
||||||
|
|
@ -135,6 +135,7 @@ class ApiLessonSessionsController < ApiController
|
||||||
end
|
end
|
||||||
|
|
||||||
msg = ChatMessage.create(me, nil, '', ChatMessage::CHANNEL_LESSON, nil, other, @lesson_session, 'JamKazam Recording', nil, claimed_recording)
|
msg = ChatMessage.create(me, nil, '', ChatMessage::CHANNEL_LESSON, nil, other, @lesson_session, 'JamKazam Recording', nil, claimed_recording)
|
||||||
|
UserMailer.lesson_attachment(me, other, @lesson_session, claimed_recording)
|
||||||
end
|
end
|
||||||
|
|
||||||
render :json => {}, :status => 200
|
render :json => {}, :status => 200
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,8 @@ class ApiMusicNotationsController < ApiController
|
||||||
end
|
end
|
||||||
|
|
||||||
msg = ChatMessage.create(me, nil, '', ChatMessage::CHANNEL_LESSON, nil, other, lesson_session, purpose, music_notation)
|
msg = ChatMessage.create(me, nil, '', ChatMessage::CHANNEL_LESSON, nil, other, lesson_session, purpose, music_notation)
|
||||||
|
|
||||||
|
UserMailer.lesson_attachment(me, other, lesson_session, music_notation)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -955,10 +955,13 @@ class ApiUsersController < ApiController
|
||||||
end
|
end
|
||||||
|
|
||||||
def lookup_user
|
def lookup_user
|
||||||
|
|
||||||
User.includes([{musician_instruments: :instrument},
|
User.includes([{musician_instruments: :instrument},
|
||||||
{band_musicians: :user},
|
{band_musicians: :user},
|
||||||
{genre_players: :genre},
|
{genre_players: :genre},
|
||||||
:bands, :instruments, :genres, :jam_track_rights, :affiliate_partner])
|
:bands, :instruments, :genres, :jam_track_rights,
|
||||||
|
:affiliate_partner, :reviews, :review_summary, :recordings,
|
||||||
|
:teacher => [:subjects, :instruments, :languages, :genres, :teachers_languages, :experiences_teaching, :experiences_award, :experiences_education, :reviews, :review_summary]])
|
||||||
.find(params[:id])
|
.find(params[:id])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -380,7 +380,8 @@ class UsersController < ApplicationController
|
||||||
js =<<JS
|
js =<<JS
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$(function() {
|
$(function() {
|
||||||
JK.GA.trackJKSocial(JK.GA.Categories.jkLike, '#{service}', 'email');
|
// // removed because we are juggling 20 events max in GA
|
||||||
|
// JK.GA.trackJKSocial(JK.GA.Categories.jkLike, '#{service}', 'email');
|
||||||
window.location = "#{url}";
|
window.location = "#{url}";
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -77,4 +77,8 @@ module MusicSessionHelper
|
||||||
def pretty_scheduled_start(music_session, with_timezone, shorter = false)
|
def pretty_scheduled_start(music_session, with_timezone, shorter = false)
|
||||||
music_session.pretty_scheduled_start(with_timezone, shorter)
|
music_session.pretty_scheduled_start(with_timezone, shorter)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def pretty_scheduled_start_slot(slot, with_timezone)
|
||||||
|
slot.pretty_scheduled_start(with_timezone)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@ object @lesson_session
|
||||||
attributes :id, :lesson_booking_id, :lesson_type, :duration, :price, :teacher_complete, :student_complete,
|
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,
|
: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,
|
: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
|
:analysed, :school_on_school?, :teacher_id, :student_id, :pretty_scheduled_start, :scheduled_start, :teacher_short_canceled,
|
||||||
|
:best_display_time
|
||||||
|
|
||||||
node do |lesson_session|
|
node do |lesson_session|
|
||||||
{
|
{
|
||||||
|
|
@ -17,6 +18,15 @@ child(:lesson_booking => :lesson_booking) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
child(:counter_slot => :counter_slot) {
|
||||||
|
attributes :id, :preferred_day, :day_of_week, :hour, :minute, :slot_type, :pretty_scheduled_start, :message, :pretty_start_time, :proposer_id, :is_student_created?, :is_teacher_created?, :timezone, :pretty_timezone
|
||||||
|
node :pretty_scheduled_start_with_timezone do |slot|
|
||||||
|
pretty_scheduled_start_slot(slot, true)
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
child(:music_session => :music_session) {
|
child(:music_session => :music_session) {
|
||||||
attributes :id, :music_session_id, :name, :description, :musician_access, :approval_required, :fan_access, :fan_chat,
|
attributes :id, :music_session_id, :name, :description, :musician_access, :approval_required, :fan_access, :fan_chat,
|
||||||
:band_id, :user_id, :genre_id, :created_at, :like_count, :comment_count, :play_count, :scheduled_duration,
|
:band_id, :user_id, :genre_id, :created_at, :like_count, :comment_count, :play_count, :scheduled_duration,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ node :next do |page|
|
||||||
end
|
end
|
||||||
|
|
||||||
node :entries do |page|
|
node :entries do |page|
|
||||||
partial "api_users/show", object: @users
|
partial "api_users/show_teacher_index", object: @users
|
||||||
end
|
end
|
||||||
|
|
||||||
node :total_entries do |page|
|
node :total_entries do |page|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
object @user
|
||||||
|
|
||||||
|
|
||||||
|
attributes :id, :first_name, :last_name, :name, :photo_url
|
||||||
|
|
||||||
|
child :teacher do |teacher|
|
||||||
|
attributes :id, :biography, :school_id
|
||||||
|
end
|
||||||
|
|
@ -14,25 +14,7 @@ script type='text/template' id='template-lesson-session-actions'
|
||||||
|
|
||||||
li data-lesson-option="attach-audio"
|
li data-lesson-option="attach-audio"
|
||||||
a href='#' Attach Audio File
|
a href='#' Attach Audio File
|
||||||
= '{% } else if (data.isRequested) { %}'
|
= '{% } else if (data.isActive) { %}'
|
||||||
ul
|
|
||||||
li data-lesson-option="status"
|
|
||||||
a href='#' View Status
|
|
||||||
|
|
||||||
li data-lesson-option="messages"
|
|
||||||
a href='#' View Messages
|
|
||||||
|
|
||||||
li data-lesson-option="messages"
|
|
||||||
a href='#' Attach Message
|
|
||||||
|
|
||||||
li data-lesson-option="cancel"
|
|
||||||
= '{% if (data.isStudent) { %}'
|
|
||||||
a href='#' Cancel Request
|
|
||||||
= '{% } else { %}'
|
|
||||||
a href='#' Decline Request
|
|
||||||
= '{% } %}'
|
|
||||||
|
|
||||||
= '{% } else if (data.isScheduled) { %}'
|
|
||||||
ul
|
ul
|
||||||
li data-lesson-option="status"
|
li data-lesson-option="status"
|
||||||
a href='#' View Status
|
a href='#' View Status
|
||||||
|
|
@ -67,6 +49,23 @@ script type='text/template' id='template-lesson-session-actions'
|
||||||
li data-lesson-option="start-65-ago"
|
li data-lesson-option="start-65-ago"
|
||||||
a href='#' Set Start 65 Min Ago
|
a href='#' Set Start 65 Min Ago
|
||||||
= '{% } %}'
|
= '{% } %}'
|
||||||
|
= '{% } else if (data.isRequested) { %}'
|
||||||
|
ul
|
||||||
|
li data-lesson-option="status"
|
||||||
|
a href='#' View Status
|
||||||
|
|
||||||
|
li data-lesson-option="messages"
|
||||||
|
a href='#' View Messages
|
||||||
|
|
||||||
|
li data-lesson-option="messages"
|
||||||
|
a href='#' Attach Message
|
||||||
|
|
||||||
|
li data-lesson-option="cancel"
|
||||||
|
= '{% if (data.isStudent) { %}'
|
||||||
|
a href='#' Cancel Request
|
||||||
|
= '{% } else { %}'
|
||||||
|
a href='#' Decline Request
|
||||||
|
= '{% } %}'
|
||||||
|
|
||||||
= '{% } else { %}'
|
= '{% } else { %}'
|
||||||
ul
|
ul
|
||||||
|
|
|
||||||
|
|
@ -16,14 +16,13 @@
|
||||||
.wizard-step{ 'layout-wizard-step' => "1", 'dialog-title' => "Select & Test Audio Gear", 'dialog-purpose' => "SelectAudioGear" }
|
.wizard-step{ 'layout-wizard-step' => "1", 'dialog-title' => "Select & Test Audio Gear", 'dialog-purpose' => "SelectAudioGear" }
|
||||||
.ftuesteps
|
.ftuesteps
|
||||||
.clearall
|
.clearall
|
||||||
.help-text In this step, you will select, configure, and test your audio gear. Please watch the video for best instructions.
|
.help-text In this step, you will select, configure, and test your audio gear. Please click Instructions button for guidance.
|
||||||
.wizard-step-content
|
.wizard-step-content
|
||||||
.wizard-step-column
|
.wizard-step-column
|
||||||
%h2 Instructions
|
%h2 Instructions
|
||||||
.ftue-box.instructions
|
.ftue-box.instructions
|
||||||
%ul
|
%ul
|
||||||
%li Select audio interface for inputs and outputs.
|
%li Select audio interface for inputs and outputs.
|
||||||
%li Check input ports to which you will connect instruments or mics.
|
|
||||||
%li Check output ports you will use to monitor.
|
%li Check output ports you will use to monitor.
|
||||||
%li Configure interface settings.
|
%li Configure interface settings.
|
||||||
%li View test results.
|
%li View test results.
|
||||||
|
|
|
||||||
|
|
@ -54,3 +54,4 @@
|
||||||
= render 'dialogs/cancelLessonDialog'
|
= render 'dialogs/cancelLessonDialog'
|
||||||
= render 'dialogs/rescheduleLessonDialog'
|
= render 'dialogs/rescheduleLessonDialog'
|
||||||
= render 'dialogs/rateUserDialog'
|
= render 'dialogs/rateUserDialog'
|
||||||
|
= render 'dialogs/musicNotationUploadDialog'
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
.dialog.dialog-overlay-sm.top-parent layout='dialog' layout-id='music-notation-upload-dialog' id='music-notation-upload-dialog'
|
||||||
|
= react_component 'MusicNotationUploadDialog', {}
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
%a{:hoveraction => "{{data.feed_item.helpers.artist_hoveraction}}", :profileaction => "{{data.feed_item.helpers.artist_hoveraction}}", :"{{data.feed_item.helpers.artist_datakey}}" => "{{data.feed_item.helpers.artist_id}}"}
|
%a{:hoveraction => "{{data.feed_item.helpers.artist_hoveraction}}", :profileaction => "{{data.feed_item.helpers.artist_hoveraction}}", :"{{data.feed_item.helpers.artist_datakey}}" => "{{data.feed_item.helpers.artist_id}}"}
|
||||||
%img{ src: '{{data.feed_item.helpers.avatar}}' }
|
%img{ src: '{{data.feed_item.helpers.avatar}}' }
|
||||||
/ type and artist
|
/ type and artist
|
||||||
.left.ml20.w15
|
.left.ml20.w15.title-artist
|
||||||
.title
|
.title
|
||||||
%a.title-text{:href => "/recordings/{{data.candidate_claimed_recording.id}}", :rel => "external", :hoveraction => "recording", :'recording-id' => '{{data.candidate_claimed_recording.id}}'} RECORDING
|
%a.title-text{:href => "/recordings/{{data.candidate_claimed_recording.id}}", :rel => "external", :hoveraction => "recording", :'recording-id' => '{{data.candidate_claimed_recording.id}}'} RECORDING
|
||||||
%a.edit-recording-dialog{href: "#"} (edit)
|
%a.edit-recording-dialog{href: "#"} (edit)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
require 'factory_girl'
|
require 'factory_girl'
|
||||||
require 'timecop'
|
require 'timecop'
|
||||||
require 'rspec-rails'
|
require 'rspec-rails'
|
||||||
|
begin
|
||||||
require Rails.root.join('spec', 'support', 'lessons.rb')
|
require Rails.root.join('spec', 'support', 'lessons.rb')
|
||||||
|
rescue LoadError
|
||||||
|
puts "for production; we ignore LoadError"
|
||||||
|
end
|
||||||
|
|
||||||
namespace :lessons do
|
namespace :lessons do
|
||||||
|
|
||||||
|
|
@ -77,8 +81,8 @@ namespace :lessons do
|
||||||
end
|
end
|
||||||
|
|
||||||
task book_test_drive: :environment do |task, args|
|
task book_test_drive: :environment do |task, args|
|
||||||
user = User.find_by_email(ENV['STUDENT_EMAIL'])
|
user = User.find_by_email(ENV['STUDENT'])
|
||||||
teacher = User.find_by_email(ENV['TEACHER_EMAIL'])
|
teacher = User.find_by_email(ENV['TEACHER'])
|
||||||
|
|
||||||
|
|
||||||
slots = []
|
slots = []
|
||||||
|
|
@ -91,7 +95,6 @@ namespace :lessons do
|
||||||
user.save!
|
user.save!
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
booking = LessonBooking.book_test_drive(user, teacher, slots, "Hey I've heard of you before.")
|
booking = LessonBooking.book_test_drive(user, teacher, slots, "Hey I've heard of you before.")
|
||||||
if booking.errors.any?
|
if booking.errors.any?
|
||||||
puts booking.errors.inspect
|
puts booking.errors.inspect
|
||||||
|
|
@ -100,7 +103,7 @@ namespace :lessons do
|
||||||
lesson = booking.lesson_sessions[0]
|
lesson = booking.lesson_sessions[0]
|
||||||
|
|
||||||
if user.most_recent_test_drive_purchase.nil?
|
if user.most_recent_test_drive_purchase.nil?
|
||||||
LessonPackagePurchase.create(user, lesson.booking, LessonPackageType.test_drive_4)
|
LessonPackagePurchase.create(user, lesson.lesson_booking, LessonPackageType.test_drive_4)
|
||||||
end
|
end
|
||||||
|
|
||||||
#lesson.accept({message: 'Yeah I got this', slot: slots[0]})
|
#lesson.accept({message: 'Yeah I got this', slot: slots[0]})
|
||||||
|
|
@ -109,6 +112,6 @@ namespace :lessons do
|
||||||
#lesson.slot.should eql slots[0]
|
#lesson.slot.should eql slots[0]
|
||||||
#lesson.status.should eql LessonSession::STATUS_APPROVED
|
#lesson.status.should eql LessonSession::STATUS_APPROVED
|
||||||
|
|
||||||
puts "http://localhost:3000/client#/jamclass/lesson-booking/#{lesson.booking.id}"
|
puts "http://localhost:3000/client#/jamclass/lesson-booking/#{lesson.lesson_booking.id}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,21 @@ describe ApiTeachersController do
|
||||||
Teacher.destroy_all
|
Teacher.destroy_all
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "index" do
|
||||||
|
it "simple" do
|
||||||
|
@teacher = Teacher.save_teacher(
|
||||||
|
user,
|
||||||
|
years_teaching: 21,
|
||||||
|
biography: BIO,
|
||||||
|
genres: [genre1, genre2],
|
||||||
|
instruments: [instrument1, instrument2],
|
||||||
|
languages: [language1, language2],
|
||||||
|
subjects: [subject1, subject2]
|
||||||
|
)
|
||||||
|
|
||||||
|
get :index
|
||||||
|
end
|
||||||
|
end
|
||||||
describe "creates" do
|
describe "creates" do
|
||||||
it "simple" do
|
it "simple" do
|
||||||
post :create, biography: BIO, format: 'json'
|
post :create, biography: BIO, format: 'json'
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ describe "Test Drive", :js => true, :type => :feature, :capybara_feature => true
|
||||||
|
|
||||||
# we tell user they have test drive purchased, and take them to the teacher screen
|
# we tell user they have test drive purchased, and take them to the teacher screen
|
||||||
find('#banner h1', text: 'Test Drive Purchased')
|
find('#banner h1', text: 'Test Drive Purchased')
|
||||||
find('#banner .dialog-inner', text: "You have purchased #{4} TestDrive credits and have used 1 credit it to request a JamClass with #{teacher_user.name}")
|
find('#banner .dialog-inner', text: "You have purchased #{4} TestDrive credits and have used 1 credit to request a JamClass with #{teacher_user.name}")
|
||||||
|
|
||||||
# dismiss banner
|
# dismiss banner
|
||||||
find('a.button-orange', text:'CLOSE').trigger(:click)
|
find('a.button-orange', text:'CLOSE').trigger(:click)
|
||||||
|
|
|
||||||
|
|
@ -118,4 +118,95 @@ describe "JamClassScreen", :js => true, :type => :feature, :capybara_feature =>
|
||||||
find('#jam-class-student-screen td.displayStatusColumn', text: 'Canceled (Teacher)')
|
find('#jam-class-student-screen td.displayStatusColumn', text: 'Canceled (Teacher)')
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "student cancels one of recurring" do
|
||||||
|
lesson = monthly_lesson(user, teacher_user, {accept: true})
|
||||||
|
lesson1 = lesson.lesson_booking.lesson_sessions[0]
|
||||||
|
lesson2 = lesson.lesson_booking.lesson_sessions[1]
|
||||||
|
|
||||||
|
fast_signin(user, "/client#/jamclass")
|
||||||
|
find('tr[data-lesson-session-id="' + lesson1.id + '"] td.displayStatusColumn', text: 'Scheduled')
|
||||||
|
find('tr[data-lesson-session-id="' + lesson2.id + '"] td.displayStatusColumn', text: 'Scheduled')
|
||||||
|
|
||||||
|
# open up hover
|
||||||
|
find('tr[data-lesson-session-id="' + lesson1.id + '"] .lesson-session-actions-btn').trigger(:click)
|
||||||
|
find('li[data-lesson-option="cancel"] a', visible: false, text: 'Cancel Lesson').trigger(:click)
|
||||||
|
# confirm cancelation -
|
||||||
|
find('#banner a', text: 'CANCEL THIS LESSON').trigger(:click)
|
||||||
|
find('tr[data-lesson-session-id="' + lesson1.id + '"] td.displayStatusColumn', text: 'Canceled (Student)')
|
||||||
|
find('tr[data-lesson-session-id="' + lesson2.id + '"] td.displayStatusColumn', text: 'Scheduled')
|
||||||
|
|
||||||
|
switch_user(teacher_user, "/client#/jamclass")
|
||||||
|
|
||||||
|
find('tr[data-lesson-session-id="' + lesson1.id + '"] td.displayStatusColumn', text: 'Canceled (Student)')
|
||||||
|
find('tr[data-lesson-session-id="' + lesson2.id + '"] td.displayStatusColumn', text: 'Scheduled')
|
||||||
|
end
|
||||||
|
|
||||||
|
it "student cancels all recurring" do
|
||||||
|
lesson = monthly_lesson(user, teacher_user, {accept: true})
|
||||||
|
lesson1 = lesson.lesson_booking.lesson_sessions[0]
|
||||||
|
lesson2 = lesson.lesson_booking.lesson_sessions[1]
|
||||||
|
lesson1.recurring.should be_true
|
||||||
|
lesson.lesson_booking.recurring.should be_true
|
||||||
|
|
||||||
|
fast_signin(user, "/client#/jamclass")
|
||||||
|
find('tr[data-lesson-session-id="' + lesson1.id + '"] td.displayStatusColumn', text: 'Scheduled')
|
||||||
|
find('tr[data-lesson-session-id="' + lesson2.id + '"] td.displayStatusColumn', text: 'Scheduled')
|
||||||
|
|
||||||
|
# open up hover
|
||||||
|
find('tr[data-lesson-session-id="' + lesson1.id + '"] .lesson-session-actions-btn').trigger(:click)
|
||||||
|
find('li[data-lesson-option="cancel"] a', visible: false, text: 'Cancel Lesson').trigger(:click)
|
||||||
|
# confirm cancelation -
|
||||||
|
find('#banner a', text: 'CANCEL ALL LESSONS').trigger(:click)
|
||||||
|
find('tr[data-lesson-session-id="' + lesson1.id + '"] td.displayStatusColumn', text: 'Canceled (Student)')
|
||||||
|
lesson1.reload
|
||||||
|
lesson2.reload
|
||||||
|
lesson1.status.should eql LessonSession::STATUS_CANCELED
|
||||||
|
lesson2.status.should eql LessonSession::STATUS_CANCELED
|
||||||
|
|
||||||
|
find('tr[data-lesson-session-id="' + lesson2.id + '"] td.displayStatusColumn', text: 'Canceled (Student)')
|
||||||
|
|
||||||
|
switch_user(teacher_user, "/client#/jamclass")
|
||||||
|
|
||||||
|
find('tr[data-lesson-session-id="' + lesson1.id + '"] td.displayStatusColumn', text: 'Canceled (Student)')
|
||||||
|
find('tr[data-lesson-session-id="' + lesson2.id + '"] td.displayStatusColumn', text: 'Canceled (Student)')
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "counter" do
|
||||||
|
it "test drive" do
|
||||||
|
lesson = testdrive_lesson(user, teacher_user, {accept: false})
|
||||||
|
|
||||||
|
fast_signin(teacher_user, "/client#/jamclass")
|
||||||
|
validate_status(lesson, 'Requested')
|
||||||
|
|
||||||
|
jamclass_hover_option('reschedule', 'Reschedule Lesson')
|
||||||
|
|
||||||
|
# no popup should show in this case, because it's not yet scheduled
|
||||||
|
|
||||||
|
# we should be at lesson status page
|
||||||
|
find('h2', text: 'respond to lesson request')
|
||||||
|
|
||||||
|
screenshot
|
||||||
|
|
||||||
|
counter_day
|
||||||
|
|
||||||
|
# switch to student
|
||||||
|
|
||||||
|
switch_user(user, "/client#/jamclass")
|
||||||
|
validate_status(lesson, 'Requested')
|
||||||
|
jamclass_hover_option('status', 'View Status')
|
||||||
|
|
||||||
|
find('h2', text: 'this lesson is coming up soon')
|
||||||
|
|
||||||
|
screenshot
|
||||||
|
|
||||||
|
approve_lesson(lesson)
|
||||||
|
|
||||||
|
jamclass_hover_option('reschedule', 'Reschedule Lesson')
|
||||||
|
find('#banner h1', text: 'Lesson Change Requested')
|
||||||
|
find('#banner .close-btn').trigger(:click)
|
||||||
|
|
||||||
|
counter_day
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ describe "Lesson Booking Status page", :js => true, :type => :feature, :capybara
|
||||||
|
|
||||||
find('h2', text: 'your lesson has been requested')
|
find('h2', text: 'your lesson has been requested')
|
||||||
|
|
||||||
|
find('p.proposing-new-time')
|
||||||
screenshot
|
screenshot
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -32,11 +33,14 @@ describe "Lesson Booking Status page", :js => true, :type => :feature, :capybara
|
||||||
it "approved" do
|
it "approved" do
|
||||||
lesson = testdrive_lesson(user, teacher, {accept:true, finish:false})
|
lesson = testdrive_lesson(user, teacher, {accept:true, finish:false})
|
||||||
|
|
||||||
fast_signin(user, "/client#/jamclass/lesson-booking/" + lesson.id)
|
lesson.reload
|
||||||
|
lesson.status.should
|
||||||
|
lesson.lesson_booking.accepter_id.should_not be_nil
|
||||||
|
fast_signin(user, "/client#/jamclass/lesson-booking/" + lesson.id )
|
||||||
|
|
||||||
find('h2', text: 'this lesson is coming up soon')
|
find('h2', text: 'this lesson is coming up soon')
|
||||||
|
|
||||||
find('p.lesson-time', "will take place each")
|
find('p.generic-time-stmt')
|
||||||
screenshot
|
screenshot
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -185,4 +189,90 @@ describe "Lesson Booking Status page", :js => true, :type => :feature, :capybara
|
||||||
find('#lesson-booking', text: 'US Central Time')
|
find('#lesson-booking', text: 'US Central Time')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "requested recurring with focused lesson" do
|
||||||
|
lesson = monthly_lesson(user, teacher, {accept: false})
|
||||||
|
|
||||||
|
fast_signin(user, "/client#/jamclass/lesson-booking/" + lesson.id)
|
||||||
|
|
||||||
|
find('.request-sent', text: 'Your request has been sent.')
|
||||||
|
page.should_not have_selector('p.proposing-new-time')
|
||||||
|
|
||||||
|
# the teacher can either accept or propose a new time, so they see both
|
||||||
|
switch_user(teacher, "/client#/jamclass/lesson-booking/" + lesson.id)
|
||||||
|
|
||||||
|
find('p.action', text: 'Has requested')
|
||||||
|
page.should_not have_selector('p.generic-time-stmt')
|
||||||
|
|
||||||
|
find(".slot-decision-field[data-slot-id=\"#{lesson.lesson_booking.default_slot.id}\"] ins", visible: false).trigger(:click)
|
||||||
|
find('.schedule.button-orange').trigger(:click)
|
||||||
|
find('tr[data-lesson-session-id="' + lesson.id + '"] td.displayStatusColumn', text: 'Scheduled')
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
it "requested recurring with unfocused lesson" do
|
||||||
|
lesson = monthly_lesson(user, teacher, {accept: false})
|
||||||
|
|
||||||
|
fast_signin(user, "/client#/jamclass/lesson-booking/" + lesson.lesson_booking.id)
|
||||||
|
|
||||||
|
find('.request-sent', text: 'Your request has been sent.')
|
||||||
|
|
||||||
|
page.should_not have_selector('p.proposing-new-time')
|
||||||
|
|
||||||
|
switch_user(teacher, "/client#/jamclass/lesson-booking/" + lesson.id)
|
||||||
|
|
||||||
|
find('p.action', text: 'Has requested')
|
||||||
|
page.should_not have_selector('p.generic-time-stmt')
|
||||||
|
|
||||||
|
find(".slot-decision-field[data-slot-id=\"#{lesson.lesson_booking.default_slot.id}\"] ins", visible: false).trigger(:click)
|
||||||
|
find('.schedule.button-orange').trigger(:click)
|
||||||
|
find('tr[data-lesson-session-id="' + lesson.id + '"] td.displayStatusColumn', text: 'Scheduled')
|
||||||
|
end
|
||||||
|
|
||||||
|
it "accepted recurring with focused lesson" do
|
||||||
|
lesson = monthly_lesson(user, teacher, {accept: true})
|
||||||
|
|
||||||
|
fast_signin(user, "/client#/jamclass/lesson-booking/" + lesson.id + "_rescheduling")
|
||||||
|
|
||||||
|
find('p.proposing-new-time')
|
||||||
|
|
||||||
|
# the teacher can either accept or propose a new time, so they see both
|
||||||
|
switch_user(teacher, "/client#/jamclass/lesson-booking/" + lesson.id + "_rescheduling")
|
||||||
|
|
||||||
|
find('p.proposing-new-time')
|
||||||
|
|
||||||
|
# change the lesson time
|
||||||
|
fill_in "alt-date-input", with: date_picker_format(Date.new(Date.today.year, Date.today.month + 1, 17))
|
||||||
|
find('td a', text: '17').trigger(:click)
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
find('.schedule.button-orange').trigger(:click)
|
||||||
|
find('#banner h1', text: 'Lesson Change Requested')
|
||||||
|
find('#banner .close-btn').trigger(:click)
|
||||||
|
find('tr[data-lesson-session-id="' + lesson.id + '"] td.displayStatusColumn', text: 'Requested')
|
||||||
|
|
||||||
|
switch_user(user, "/client#/jamclass")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "accepted recurring with unfocused lesson" do
|
||||||
|
lesson = monthly_lesson(user, teacher, {accept: true})
|
||||||
|
|
||||||
|
fast_signin(user, "/client#/jamclass/lesson-booking/" + lesson.lesson_booking.id + "_rescheduling")
|
||||||
|
|
||||||
|
find('.request-sent', text: 'Your request has been sent.')
|
||||||
|
|
||||||
|
page.should_not have_selector('p.proposing-new-time')
|
||||||
|
|
||||||
|
switch_user(teacher, "/client#/jamclass/lesson-booking/" + lesson.lesson_booking.id + "_rescheduling")
|
||||||
|
|
||||||
|
find('p.proposing-new-time')
|
||||||
|
|
||||||
|
fill_in "alt-date-input", with: date_picker_format(Date.new(Date.today.year, Date.today.month + 1, 17))
|
||||||
|
find('td a', text: '17').trigger(:click)
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
find(".slot-decision-field[data-slot-id=\"#{lesson.lesson_booking.default_slot.id}\"] ins", visible: false).trigger(:click)
|
||||||
|
find('.schedule.button-orange').trigger(:click)
|
||||||
|
find('tr[data-lesson-session-id="' + lesson.id + '"] td.displayStatusColumn', text: 'Scheduled')
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
@ -29,7 +29,7 @@ describe "Student Landing", :js => true, :type => :feature, :capybara_feature =>
|
||||||
|
|
||||||
fill_in "email", with: 'student_123@jamkazam.com'
|
fill_in "email", with: 'student_123@jamkazam.com'
|
||||||
fill_in "password", with: 'jam123'
|
fill_in "password", with: 'jam123'
|
||||||
find('.register-area ins', visible: false) .trigger(:click)
|
find('.register-area ins', visible: false).trigger(:click)
|
||||||
find('button.cta-button', text: 'SIGN UP').trigger(:click)
|
find('button.cta-button', text: 'SIGN UP').trigger(:click)
|
||||||
|
|
||||||
# this should show on the /client#/home page (WILL CHANGE)
|
# this should show on the /client#/home page (WILL CHANGE)
|
||||||
|
|
|
||||||
|
|
@ -25,12 +25,16 @@ def teacher_approve(lesson_session)
|
||||||
sign_out_poltergeist(validate: true)
|
sign_out_poltergeist(validate: true)
|
||||||
sign_in_poltergeist(lesson_session.teacher, password: 'foobar')
|
sign_in_poltergeist(lesson_session.teacher, password: 'foobar')
|
||||||
visit "/client#/jamclass/lesson-booking/" + lesson_session.id
|
visit "/client#/jamclass/lesson-booking/" + lesson_session.id
|
||||||
|
find(".slot-decision-field[data-slot-id=\"#{lesson_session.lesson_booking.default_slot.id}\"] ins", visible: false).trigger(:click)
|
||||||
find('.schedule.button-orange').trigger(:click)
|
find('.schedule.button-orange').trigger(:click)
|
||||||
# dismiss banner
|
# dismiss banner
|
||||||
#find('a.button-orange', text:'CLOSE').trigger(:click)
|
#find('a.button-orange', text:'CLOSE').trigger(:click)
|
||||||
find('tr[data-lesson-session-id="' + lesson_session.id + '"] .displayStatusColumn', text: 'Scheduled')
|
find('tr[data-lesson-session-id="' + lesson_session.id + '"] .displayStatusColumn', text: 'Scheduled')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def date_picker_format(date)
|
||||||
|
date.strftime('%a %b %d %Y')
|
||||||
|
end
|
||||||
def fill_out_single_lesson
|
def fill_out_single_lesson
|
||||||
|
|
||||||
find('h2', text: 'book testdrive lesson')
|
find('h2', text: 'book testdrive lesson')
|
||||||
|
|
@ -56,6 +60,33 @@ def fill_out_single_lesson
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def validate_status(lesson, expectedStatus)
|
||||||
|
find('tr[data-lesson-session-id="' + lesson.id + '"] td.displayStatusColumn', text: expectedStatus)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def jamclass_hover_option(lesson, option, text)
|
||||||
|
# open up hover
|
||||||
|
find('tr[data-lesson-session-id="' + lesson.id + '"] .lesson-session-actions-btn').trigger(:click)
|
||||||
|
find('li[data-lesson-option="' + option + '"] a', visible: false, text: text).trigger(:click)
|
||||||
|
end
|
||||||
|
|
||||||
|
def counter_day
|
||||||
|
fill_in "alt-date-input", with: date_picker_format(Date.new(Date.today.year, Date.today.month + 1, 17))
|
||||||
|
find('td a', text: '17').trigger(:click)
|
||||||
|
sleep 3
|
||||||
|
find('.schedule.button-orange').trigger(:click)
|
||||||
|
find('#banner h1', text: 'Lesson Change Requested')
|
||||||
|
find('#banner .close-btn').trigger(:click)
|
||||||
|
find('tr[data-lesson-session-id="' + lesson.id + '"] td.displayStatusColumn', text: 'Requested')
|
||||||
|
end
|
||||||
|
|
||||||
|
def approve_lesson(lesson, slot = lesson.lesson_booking.default_slot)
|
||||||
|
find(".slot-decision-field[data-slot-id=\"#{slot.id}\"] ins", visible: false).trigger(:click)
|
||||||
|
find('.schedule.button-orange').trigger(:click)
|
||||||
|
find('tr[data-lesson-session-id="' + lesson.id + '"] td.displayStatusColumn', text: 'Scheduled')
|
||||||
|
end
|
||||||
|
|
||||||
def fill_out_payment(expected = nil)
|
def fill_out_payment(expected = nil)
|
||||||
|
|
||||||
fill_in 'card-number', with: '4111111111111111'
|
fill_in 'card-number', with: '4111111111111111'
|
||||||
|
|
@ -159,7 +190,7 @@ def book_lesson(user, teacher, options)
|
||||||
end
|
end
|
||||||
|
|
||||||
if options[:accept]
|
if options[:accept]
|
||||||
lesson.accept({message: 'Yeah I got this', slot: slots[0]})
|
lesson.accept({message: 'Yeah I got this', slot: slots[0], accepter: teacher})
|
||||||
lesson.errors.any?.should be_false unless options[:no_validate]
|
lesson.errors.any?.should be_false unless options[:no_validate]
|
||||||
lesson.reload
|
lesson.reload
|
||||||
lesson.slot.should eql slots[0] unless options[:no_validate]
|
lesson.slot.should eql slots[0] unless options[:no_validate]
|
||||||
|
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
Loading…
Reference in New Issue