teacher search and cancel powers for slow teacher responsens

This commit is contained in:
Seth Call 2018-01-27 16:18:04 -06:00
parent badea60021
commit 91da29088f
29 changed files with 427 additions and 121 deletions

View File

@ -108,7 +108,7 @@ end
group :development, :test do
gem 'capybara'
gem 'rspec-rails', '2.14.2'
gem 'rspec-rails' #, '2.14.2'
gem 'jasmine', '1.3.1'
gem 'execjs', '1.4.0'
#gem 'therubyracer' #, '0.11.0beta8'

View File

@ -92,7 +92,7 @@ GEM
aws-sdk-v1 (1.67.0)
json (~> 1.4)
nokogiri (~> 1)
backports (3.11.0)
backports (3.11.1)
bcrypt (3.1.11)
bcrypt-ruby (3.0.1)
binding_of_caller (0.8.0)
@ -100,7 +100,7 @@ GEM
bootstrap-sass (2.0.4.0)
bootstrap-will_paginate (0.0.6)
will_paginate
bugsnag (6.6.1)
bugsnag (6.6.3)
concurrent-ruby (~> 1.0)
builder (3.2.3)
cabin (0.9.0)
@ -147,7 +147,7 @@ GEM
crass (1.0.3)
database_cleaner (1.6.2)
debug_inspector (0.0.3)
devise (4.4.0)
devise (4.4.1)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 4.1.0, < 5.2)
@ -160,7 +160,7 @@ GEM
email_validator (1.6.0)
activemodel
erubis (2.7.0)
et-orbi (1.0.8)
et-orbi (1.0.9)
tzinfo
eventmachine (1.2.3)
excon (0.60.0)
@ -173,7 +173,7 @@ GEM
railties (>= 3.0.0)
faker (1.3.0)
i18n (~> 0.5)
faraday (0.13.1)
faraday (0.14.0)
multipart-post (>= 1.2, < 3)
ffi (1.9.18)
fission (0.5.0)
@ -314,10 +314,10 @@ GEM
domain_name (~> 0.5)
httparty (0.15.6)
multi_xml (>= 0.5.2)
i18n (0.9.1)
i18n (0.9.3)
concurrent-ruby (~> 1.0)
inflecto (0.0.2)
influxdb (0.5.2)
influxdb (0.5.3)
influxdb-rails (0.4.3)
influxdb (~> 0.5.0)
railties (> 3)
@ -374,7 +374,7 @@ GEM
mimemagic (0.3.2)
mini_mime (1.0.0)
mini_portile2 (2.3.0)
minitest (5.11.1)
minitest (5.11.2)
mono_logger (1.1.0)
multi_json (1.13.1)
multi_xml (0.6.0)
@ -404,7 +404,7 @@ GEM
capybara (~> 2.1)
cliver (~> 0.3.1)
websocket-driver (>= 0.2.0)
polyamorous (1.3.2)
polyamorous (1.3.3)
activerecord (>= 3.0)
postgres-copy (0.6.0)
activerecord (>= 3.0.0)
@ -428,7 +428,7 @@ GEM
binding_of_caller (>= 0.7)
pry (>= 0.9.11)
public_suffix (3.0.1)
puma (3.11.0)
puma (3.11.2)
rack (1.6.8)
rack-protection (1.5.3)
rack
@ -464,16 +464,16 @@ GEM
thor (>= 0.18.1, < 2.0)
raindrops (0.19.0)
rake (12.3.0)
ransack (1.8.4)
ransack (1.8.6)
actionpack (>= 3.0)
activerecord (>= 3.0)
activesupport (>= 3.0)
i18n
polyamorous (~> 1.3)
polyamorous (~> 1.3.2)
rb-fsevent (0.10.2)
rb-inotify (0.9.10)
ffi (>= 0.5.0, < 2)
recurly (2.12.0)
recurly (2.12.1)
redis (4.0.1)
redis-namespace (1.6.0)
redis (>= 3.0.4)
@ -507,22 +507,27 @@ GEM
http-cookie (>= 1.0.2, < 2.0)
mime-types (>= 1.16, < 4.0)
netrc (~> 0.8)
rspec (2.14.1)
rspec-core (~> 2.14.0)
rspec-expectations (~> 2.14.0)
rspec-mocks (~> 2.14.0)
rspec-core (2.14.8)
rspec-expectations (2.14.5)
diff-lcs (>= 1.1.3, < 2.0)
rspec-mocks (2.14.6)
rspec-rails (2.14.2)
rspec (3.7.0)
rspec-core (~> 3.7.0)
rspec-expectations (~> 3.7.0)
rspec-mocks (~> 3.7.0)
rspec-core (3.7.1)
rspec-support (~> 3.7.0)
rspec-expectations (3.7.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.7.0)
rspec-mocks (3.7.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.7.0)
rspec-rails (3.7.2)
actionpack (>= 3.0)
activemodel (>= 3.0)
activesupport (>= 3.0)
railties (>= 3.0)
rspec-core (~> 2.14.0)
rspec-expectations (~> 2.14.0)
rspec-mocks (~> 2.14.0)
rspec-core (~> 3.7.0)
rspec-expectations (~> 3.7.0)
rspec-mocks (~> 3.7.0)
rspec-support (~> 3.7.0)
rspec-support (3.7.0)
ruby-protocol-buffers (1.2.2)
ruby-xz (0.2.3)
ffi (~> 1.9)
@ -589,7 +594,7 @@ GEM
tilt (2.0.8)
tzinfo (1.2.4)
thread_safe (~> 0.1)
uglifier (4.1.3)
uglifier (4.1.4)
execjs (>= 0.3.0, < 3)
unf (0.1.3)
unf_ext
@ -679,7 +684,7 @@ DEPENDENCIES
resque-retry
resque_mailer
rest-client
rspec-rails (= 2.14.2)
rspec-rails
ruby-protocol-buffers (= 1.2.2)
rubyzip
sanitize

View File

@ -5,11 +5,14 @@ ActiveAdmin.register_page "Dashboard" do
content :title => proc{ I18n.t("active_admin.dashboard") } do
div :class => "blank_slate_container", :id => "dashboard_default_message" do
span :class => "blank_slate" do
span "JamKazam Data Administration Portal"
span "JamKazam Administration Portal"
small ul do
li "Admin users are users with the admin boolean set to true"
li "Invite JamKazam users using the 'Users > Invite' menu in header"
li "Admin users are created/deleted when toggling the 'admin' flag for JamKazam users"
li link_to "Users", admin_users_path
li link_to "Teachers", admin_teachers_path
li link_to "Onboarding Management", admin_currently_onboardings_path
li link_to "Lesson Sessions", admin_lesson_sessions_path
li link_to "Slow Lesson Responses", admin_slow_responses_path
end
end
end

View File

@ -12,6 +12,8 @@ ActiveAdmin.register JamRuby::User, :as => 'Users' do
filter :created_at
filter :updated_at
includes :purchased_jam_tracks, :jam_track_rights => :jam_track, :taken_lessons => :music_session, :taught_lessons => :music_session
form :partial => "form"
show do |user|
@ -79,7 +81,9 @@ ActiveAdmin.register JamRuby::User, :as => 'Users' do
else
end
end
row "Signup" do user.created_at.to_date end
row "Signup" do
user.created_at.to_date
end
row "Assigned", :onboarder_assigned_at
row "Email 1", :onboarding_email_1_sent_at
row "Email 2", :onboarding_email_2_sent_at
@ -97,6 +101,14 @@ ActiveAdmin.register JamRuby::User, :as => 'Users' do
end
end
panel "Teacher Setting" do
attributes_table do
row :is_searchable
end
end if user.teacher
panel "Lessons" do
attributes_table do
row "Taken Lessons" do
@ -154,6 +166,24 @@ ActiveAdmin.register JamRuby::User, :as => 'Users' do
end
panel "JamTracks" do
div do
link_to "Give JamTrack", "../jam_track_rights/new"
end
attributes_table do
row "Purchased JamTracks" do
table_for user.purchased_jam_tracks.unscope(:order).order('original_artist asc', 'name asc') do
column "Artist", :original_artist
column "Name", :name
column "Can Download", :can_download, as: :boolean, hint: 'Can download is the 2.99 value (have download rights)'
column "Version", :version
column "ID", :id
end
end
end
end
active_admin_comments
end
@ -174,22 +204,14 @@ ActiveAdmin.register JamRuby::User, :as => 'Users' do
link_to user.email, resource_path(user)
end
column :admin
column :updated_at
column :created_at
column :musician do |user|
user.musician? ? true : false
end
column :city
column :state
column :country
column :first_name
column :last_name
column :birth_date
column :gender
column :email_confirmed
column :photo_url
column :session_settings
column :can_invite
end
controller do

View File

@ -49,6 +49,7 @@ ActiveAdmin.register JamRuby::JamTrackRight, :as => 'JamTrackRights' do
f.inputs 'New Jam Track Right' do
f.input :jam_track, :required=>true, collection: JamTrack.all, include_blank: false
f.input :user, :required=>true, collection: User.all, include_blank: false
f.input :can_download, :required => true, as: :boolean
end
f.actions
end

View File

@ -6,45 +6,101 @@ ActiveAdmin.register JamRuby::LessonSession, :as => 'SlowResponses' do
config.batch_actions = false
config.per_page = 100
config.paginate = true
config.filters = false
config.filters = true
scope("Slow Responses", default: true) { |scope| scope.unscoped.slow_responses }
scope("Least Time Left") { |scope| scope.unscoped.least_time_left }
filter :intervened_at_null, label: 'Not Yet Intervened', as: :boolean
scope("72 hours since last request/counter", default: true) { |scope| scope.slow_responses }
member_action :cancel_lesson, :method => :get do
result = resource.cancel_by_admin
if result.errors.any?
redirect_to :back, notice: resource.errors.inspect
else
redirect_to :back, notice: 'Canceled lesson'
end
end
member_action :intervened, :method => :get do
resource.intervened
redirect_to :back
end
index do
column "Teacher" do |lesson_session|
teacher = lesson_session.teacher
span do
link_to "#{teacher.name} (#{teacher.email})", "#{Rails.application.config.external_root_url}/client#/profile/teacher/#{teacher.id}"
column "Not Responded" do |lesson_session|
div do
if lesson_session.student_last_proposed?
"Teacher"
else
"Student"
end
end
div do
late_responder = lesson_session.late_responder
span do
link_to "#{late_responder.name}", late_responder.admin_url
end
end
div do
lesson_session.late_responder.email
end
end
column "Student" do |lesson_session|
student = lesson_session.student
span do
link_to "#{student.name} (#{student.email})", "#{Rails.application.config.external_root_url}/client#/profile/#{student.id}"
column "Proposer" do |lesson_session|
div do
if lesson_session.student_last_proposed?
"Student"
else
"Teacher"
end
end
div do
last_proposer = lesson_session.last_proposer
span do
link_to "#{last_proposer.name}", last_proposer.admin_url
end
end
div do
lesson_session.last_proposer.email
end
end
column "Type" do |lesson_session|
link_to lesson_session.display_type
column "Lesson Type" do |lesson_session|
lesson_session.display_type
end
column "Start Time" do |lesson_session|
column "Proposed Start Time" do |lesson_session|
span do
if lesson_session.music_session.nil?
raise "Lessonsesison with no id #{lesson_session.id}"
else
lesson_session.music_session.pretty_scheduled_start(true)
link_to lesson_session.music_session.pretty_scheduled_start(true), lesson_session.admin_url
end
end
end
column "Last student comms date" do |lesson_session|
lesson_session.last_student_comm_date
end
column "Days with no response" do |lesson_session|
"#{lesson_session.days_no_response} days"
if lesson_session.last_response_time.nil?
'N/A'
else
"#{((Time.now - lesson_session.last_response_time) / 24 / 3600).floor} days"
end
end
column "Num Reqs/Counters" do |lesson_session|
ChatMessage.unscoped.where(lesson_session_id: lesson_session.id).where('purpose = ? OR purpose = ?', "Lesson Requested", "New Time Proposed").count(:purpose)
end
column "Intervene" do |lesson_session|
span do
if lesson_session.intervened_at
lesson_session.intervened_at.to_date
else
link_to("intervened", intervened_admin_slow_response_path(lesson_session.id), {confirm: "Mark Lesson as 'Intervened' (i.e., email sent to by hand)?"})
end
end
end
column "Cancel Lesson" do |lesson_session|
span do
link_to("cancel", cancel_lesson_admin_slow_response_path(lesson_session.id), {confirm: "Cancel Lesson?"})
end
end
end
end

View File

@ -18,7 +18,7 @@ ActiveAdmin.register JamRuby::Teacher, :as => 'Teachers' do
end
filter :teacher_full_name_or_user_email_cont, label: 'Name', as: :string
filter :user_phantom, label: 'Phantom', as: :boolean, selected: 'false'
filter :user_phantom, label: 'Phantom ', as: :boolean, selected: 'false'
#filter :by_search_user_name, label: "Name", as: :string

View File

@ -382,4 +382,5 @@ amazon_signup.sql
age_out_sessions.sql
alter_crash_dumps.sql
onboarding.sql
better_lesson_notices.sql
better_lesson_notices.sql
teacher_search_control.sql

View File

@ -0,0 +1,4 @@
ALTER TABLE teachers ADD COLUMN is_searchable BOOLEAN NOT NULL DEFAULT true;
ALTER TABLE lesson_sessions ADD COLUMN canceled_by_admin TIMESTAMP without time zone;
ALTER TABLE lesson_bookings ADD COLUMN canceled_by_admin TIMESTAMP without time zone;
ALTER TABLE lesson_sessions ADD COLUMN intervened_at TIMESTAMP without time zone;

View File

@ -1,6 +1,9 @@
<% provide(:title, @subject) %>
<% provide(:photo_url, @lesson_booking.canceler.resolved_photo_url) %>
<% if @lesson_booking.canceler %>
<% provide(:photo_url, @lesson_booking.canceler.resolved_photo_url) %>
<% else %>
<% provide(:photo_url, "#{APP_CONFIG.external_root_url}/assets/shared/avatar_generic.png") %>
<% end %>
<% content_for :note do %>
<p>
<% if @lesson_booking.recurring %>

View File

@ -4,14 +4,27 @@
<% content_for :note do %>
<p>
This teacher has declined your lesson request.
<% if @lesson_booking.canceled_by_admin %>
This lesson was canceled by a JamKazam administrator.
<% else %>
This teacher has declined your lesson request.
<% end %>
<% if @message.present? %>
<br/><br/><%= @teacher.name %> says:
<% if @lesson_booking.canceled_by_admin %>
<br/><br/>System says:
<% else %>
<br/><br/><%= @teacher.name %> says:
<% end %>
<br/><%= @message %>
<br/>
<% end %>
<br/><br/>Click the button below to view the teacher's response.</p>
<% if @lesson_booking.canceled_by_admin %>
<br/><br/>Click the button below to view the administrator's message.</p>
<% else %>
<br/><br/>Click the button below to view the teacher's response.</p>
<% 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 RESPONSE</a>
</p>

View File

@ -1,5 +1,9 @@
<% provide(:title, @subject) %>
<% provide(:photo_url, @lesson_session.canceler.resolved_photo_url) %>
<% if @lesson_booking.canceler %>
<% provide(:photo_url, @lesson_session.canceler.resolved_photo_url) %>
<% else %>
<% provide(:photo_url, "#{APP_CONFIG.external_root_url}/assets/shared/avatar_generic.png") %>
<% end %>
<% content_for :note do %>
<p>
@ -10,7 +14,11 @@
<%= @session_date %>
<% if @message.present? %>
<br/><br/><%= @lesson_session.canceler.name %> says:
<% if @lesson_session.canceled_by_admin %>
<br/><br/>System says:
<% else %>
<br/><br/><%= @lesson_session.canceler.name %> says:
<% end %>
<br/><%= @message %>
<br/>
<% end %>

View File

@ -1,5 +1,9 @@
<% provide(:title, @subject) %>
<% provide(:photo_url, @lesson_booking.canceler.resolved_photo_url) %>
<% if @lesson_booking.canceler %>
<% provide(:photo_url, @lesson_booking.canceler.resolved_photo_url) %>
<% else %>
<% provide(:photo_url, "#{APP_CONFIG.external_root_url}/assets/shared/avatar_generic.png") %>
<% end %>
<% content_for :note do %>
<p>
@ -16,7 +20,12 @@
<% end %>
<% if @message.present? %>
<br/><br/><%= @lesson_booking.canceler.name %> says:
<% if @lesson_booking.canceled_by_admin %>
<br/><br/>System says:
<% else %>
<br/><br/><%= @lesson_booking.canceler.name %> says:
<% end %>
<br/><%= @message %>
<br/>
<% end %>

View File

@ -1,5 +1,9 @@
<% provide(:title, @subject) %>
<% provide(:photo_url, @lesson_session.canceler.resolved_photo_url) %>
<% if @lesson_booking.canceler %>
<% provide(:photo_url, @lesson_session.canceler.resolved_photo_url) %>
<% else %>
<% provide(:photo_url, "#{APP_CONFIG.external_root_url}/assets/shared/avatar_generic.png") %>
<% end %>
<% content_for :note do %>
<p>
@ -11,7 +15,11 @@
<%= @session_date %>
<% if @message.present? %>
<br/><br/><%= @lesson_session.canceler.name %> says:
<% if @lesson_session.canceled_by_admin %>
<br/><br/>System says:
<% else %>
<br/><br/><%= @lesson_session.canceler.name %> says:
<% end %>
<br/><%= @message %>
<br/>
<% end %>

View File

@ -7,7 +7,7 @@ module JamRuby
@@log = Logging.logger[JamTrackRight]
attr_accessible :user, :jam_track, :user_id, :jam_track_id, :download_count
attr_accessible :user_id, :jam_track_id, as: :admin
attr_accessible :user_id, :jam_track_id, :can_download, as: :admin
attr_accessible :url_48, :md5_48, :length_48, :url_44, :md5_44, :length_44
belongs_to :user, class_name: "JamRuby::User" # the owner, or purchaser of the jam_track
belongs_to :jam_track, class_name: "JamRuby::JamTrack"
@ -38,6 +38,8 @@ module JamRuby
key = rsa_key.to_pem()
self.private_key_44 = key
self.private_key_48 = key
self.version = jam_track.version
end
def after_save
# try to catch major transitions:

View File

@ -741,14 +741,16 @@ module JamRuby
self
end
def cancel_tracking(canceler, message)
def cancel_tracking(canceler, message, canceled_by_admin = false)
canceled_by_student = canceler == student
self.status = STATUS_CANCELED
self.cancel_message = message
self.canceler = canceler
self.canceler = canceler if !canceled_by_admin
self.canceling = true
if canceled_by_student
if canceled_by_admin
self.canceled_by_admin = Time.now
elsif canceled_by_student
self.student_canceled = true
self.student_canceled_at = Time.now
self.student_canceled_reason = message
@ -759,16 +761,16 @@ module JamRuby
end
end
def cancel(canceler, other, message)
def cancel(canceler, other, message, canceled_by_admin = false)
cancel_tracking(canceler, message)
cancel_tracking(canceler, message, canceled_by_admin)
self.active = false
success = save
if success
lesson_sessions.upcoming.each do |lesson_session|
lesson_session = LessonSession.find(lesson_session.id) # because .upcoming creates ReadOnly records
lesson_session.cancel_lesson(canceler, message)
lesson_session.cancel_lesson(canceler, message, canceled_by_admin)
if !lesson_session.save
return lesson_session
end
@ -781,7 +783,12 @@ module JamRuby
UserMailer.teacher_lesson_booking_canceled(self, message).deliver_now
purpose = "Lesson Canceled"
else
if canceler == student
if canceled_by_admin
# meh. no two way comm here. just bail
#Notification.send_lesson_message('canceled', next_lesson, true)
UserMailer.student_lesson_booking_declined(self, message).deliver_now
UserMailer.teacher_lesson_booking_canceled(self, message).deliver_now
elsif canceler == student
# if it's the first time acceptance student canceling, we call it a 'cancel'
Notification.send_lesson_message('canceled', next_lesson, false)
UserMailer.teacher_lesson_booking_canceled(self, message).deliver_now
@ -795,7 +802,9 @@ module JamRuby
end
message = '' if message.nil?
msg = ChatMessage.create(canceler, nil, message, ChatMessage::CHANNEL_LESSON, nil, other, next_lesson, purpose)
if !canceled_by_admin
msg = ChatMessage.create(canceler, nil, message, ChatMessage::CHANNEL_LESSON, nil, other, next_lesson, purpose)
end
else
end

View File

@ -79,11 +79,8 @@ module JamRuby
scope :past_cancel_window, -> { joins(:music_session).where('music_sessions.scheduled_start > ?', 24.hours.from_now) }
# show all requested/countered sessions where the student was the last to communicate
scope :slow_responses, -> { joins(:lesson_booking).where('lesson_sessions.status = ? OR lesson_sessions.status = ?', LessonSession::STATUS_REQUESTED, LessonSession::STATUS_COUNTERED)
.where('lesson_sessions.counterer_id IS NULL OR lesson_sessions.user_id = lesson_sessions.counterer_id')
.where("(? - (COALESCE(lesson_sessions.countered_at, lesson_bookings.sent_notices_at))) > INTERVAL '72 hours'", Time.now)
.order('(COALESCE(lesson_sessions.countered_at, lesson_bookings.sent_notices_at)) ASC') }
scope :least_time_left, -> { joins(:lesson_booking, :music_session).where('lesson_sessions.status = ? OR lesson_sessions.status = ?', LessonSession::STATUS_REQUESTED, LessonSession::STATUS_COUNTERED)
.where('lesson_sessions.counterer_id IS NULL OR lesson_sessions.user_id = lesson_sessions.counterer_id')
.order('music_sessions.scheduled_start DESC') }
def create_charge
if payment_if_school_on_school? && !is_test_drive? && !is_monthly_payment?
@ -222,6 +219,22 @@ module JamRuby
counterer_id.nil? || counterer_id == student_id
end
def late_responder
if student_last_proposed?
teacher
else
student
end
end
def last_proposer
if student_last_proposed?
student
else
teacher
end
end
def lesson_is_free?
lesson_booking.booked_price == 0.00
end
@ -999,15 +1012,17 @@ module JamRuby
response
end
def cancel_lesson(canceler, message)
def cancel_lesson(canceler, message, canceled_by_admin = false)
canceled_by_student = canceler == student
self.status = STATUS_CANCELED
self.cancel_message = message
self.canceler = canceler
self.canceler = canceler if !canceled_by_admin
self.canceling = true
if canceled_by_student
if canceled_by_admin
elsif canceled_by_student
self.student_canceled = true
self.student_canceled_at = Time.now
self.student_canceled_reason = message
@ -1059,6 +1074,10 @@ module JamRuby
other = canceled_by_student ? teacher : student
message = params[:message]
message = '' if message.nil?
if params[:canceled_by_admin]
self.canceled_by_admin = Time.now
end
if lesson_booking.recurring
update_all = params[:update_all]
@ -1071,7 +1090,7 @@ module JamRuby
end
if update_all
response = lesson_booking.cancel(canceler, other, message)
response = lesson_booking.cancel(canceler, other, message, params[:canceled_by_admin])
if response.errors.any?
raise ActiveRecord::Rollback
end
@ -1081,13 +1100,20 @@ module JamRuby
raise ActiveRecord::Rollback
end
else
cancel_lesson(canceler, message)
cancel_lesson(canceler, message, params[:canceled_by_admin])
if !save
response = self
raise ActiveRecord::Rollback
end
msg = ChatMessage.create(canceler, nil, message, ChatMessage::CHANNEL_LESSON, nil, other, self, "Lesson Canceled")
if canceled_by_admin
msg = ChatMessage.create(student, nil, message, ChatMessage::CHANNEL_LESSON, nil, teacher, self, "Lesson Canceled")
msg = ChatMessage.create(teacher, nil, message, ChatMessage::CHANNEL_LESSON, nil, student, self, "Lesson Canceled")
else
msg = ChatMessage.create(canceler, nil, message, ChatMessage::CHANNEL_LESSON, nil, other, self, "Lesson Canceled")
end
Notification.send_lesson_message('canceled', self, false)
Notification.send_lesson_message('canceled', self, true)
UserMailer.student_lesson_canceled(self, message).deliver_now
@ -1108,8 +1134,6 @@ module JamRuby
else
'TBD'
end
end
def timed_description
if is_test_drive?
@ -1123,6 +1147,20 @@ module JamRuby
end
end
def cancel_by_admin
self.cancel({
canceler: nil,
canceled_by_admin: true,
message: 'Canceled by JamKazam administrator',
update_all: false
})
end
def intervened
self.intervened_at = Time.now
self.save
end
def display_type
if is_test_drive?
'TestDrive'
@ -1137,15 +1175,8 @@ module JamRuby
counterer_id == user_id ? countered_at : sent_notices_at
end
def days_no_response
date = last_student_comm_date
if date.nil?
'?'
else
(Time.now - last_student_comm_date) / (60 * 60 * 24)
end
def last_response_time
[countered_at, lesson_booking.sent_notices_at].find{|x|!x.nil?}
end
def stripe_description(lesson_booking)

View File

@ -64,7 +64,7 @@ module JamRuby
query = User.unscoped.joins(:teacher)
# 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').where('teachers.is_searchable = TRUE')
# always force GuitarCenter users to see only their school's teachers, regardless of what they picked
if user && (user.is_guitar_center_student? || (params[:onlyMySchool] && params[:onlyMySchool] != 'false' && user.school_id))
@ -222,6 +222,7 @@ module JamRuby
teacher.price_per_month_120_cents = params[:price_per_month_120_cents] if params.key?(:price_per_month_120_cents)
teacher.teaches_test_drive = params[:teaches_test_drive] if params.key?(:teaches_test_drive)
teacher.test_drives_per_week = params[:test_drives_per_week] if params.key?(:test_drives_per_week)
teacher.is_searchable = params[:is_searchable] if params.key?(:is_searchable)
teacher.test_drives_per_week = 10 if !params.key?(:test_drives_per_week) # default to 10 in absence of others
if params.key?(:school_id)
teacher.school_id = params[:school_id]

View File

@ -100,6 +100,7 @@
var $btnEdit = $screen.find('.edit-profile-btn');
var $btnTeacherProfileEdit = $screen.find('.edit-teacher-profile-btn');
var $btnTeacherProfileView = $screen.find('.view-teacher-profile-btn');
var $toggleTeacherSearchEnabled = $screen.find('.teacher-search-enabled')
var $btnAddFriend = $screen.find('#btn-add-friend');
var $btnFollowUser = $screen.find('#btn-follow-user');
var $btnMessageUser = $screen.find('#btn-message-user');
@ -232,9 +233,11 @@
$btnTeacherProfileEdit.show();
if (isTeacher()) {
$btnTeacherProfileView.show();
$toggleTeacherSearchEnabled.show()
}
else {
$btnTeacherProfileView.hide();
$toggleTeacherSearchEnabled.hide()
}
$btnAddFriend.hide();
$btnFollowUser.hide();
@ -332,6 +335,11 @@
window.ProfileActions.startProfileEdit('experience', true)
return false;
})
$toggleTeacherSearchEnabled.find('input').change(function(e) {
var $check = $(this)
rest.updateTeacher({id: user.teacher.id, is_searchable: $check.is(':checked')})
.fail(app.ajaxError);
})
}
function playSoundCloudFile(e) {
@ -520,6 +528,7 @@
renderPerformanceSamples();
renderOnlinePresence();
renderInterests();
renderTeacherSearch();
}
}
@ -565,6 +574,23 @@
}
}
function renderTeacherSearch()
{
if(isTeacher())
{
if(user.teacher.is_searchable )
{
$toggleTeacherSearchEnabled.find('input').attr('checked', 'checked')
}
else
{
$toggleTeacherSearchEnabled.find('input').removeAttr('checked')
}
}
}
function renderMusicalExperience() {
profileUtils.renderMusicalExperience(user, $screen, isCurrentUser())
}

View File

@ -401,11 +401,14 @@ UserStore = context.UserStore
otherCountered: () ->
@myself().id == @counterer().id
adminCanceled: () ->
this.state.booking?.canceled_by_admin?
studentCanceled: () ->
@canceler().id == @student().id
@canceler()?.id == @student().id
selfCanceled: () ->
@canceler().id == @myself().id
@canceler()?.id == @myself().id
studentMadeDefaultSlot: () ->
@student()?.id == @defaultSlot()?.proposer_id
@ -513,7 +516,7 @@ UserStore = context.UserStore
else if @isCounter()
@counterer().id == @myself().id
else if @isCanceled()
@canceler().id == @myself().id
@canceler()?.id == @myself().id
else if @isSuspended()
@studentViewing()
else
@ -544,7 +547,9 @@ UserStore = context.UserStore
messageBlock: (selfWroteMessage, message) ->
if selfWroteMessage
if typeof selfWroteMessage == 'string'
whoSaid = selfWroteMessage + ' said:'
else if selfWroteMessage
whoSaid = "You said:"
else
whoSaid = "Message from #{this.other().first_name}:"
@ -660,12 +665,16 @@ UserStore = context.UserStore
if !photo_url?
photo_url = '/assets/shared/avatar_generic.png'
if user?
name = user.name
else
name = 'System'
`<div className="user-header">
<div className="avatar">
<img src={photo_url}/>
</div>
<div className="user-name">
{user.name}
{name}
</div>
</div>`
@ -1049,7 +1058,9 @@ UserStore = context.UserStore
initial = @neverAccepted()
if initial
if @studentViewing()
if @adminCanceled()
action = `<p>A JamKazam administrator canceled this lesson request.</p>`
else if @studentViewing()
if @studentCanceled()
action = `<p>You canceled this lesson request.</p>`
else
@ -1062,7 +1073,7 @@ UserStore = context.UserStore
if @studentViewing()
if @studentCanceled()
if @adminCanceled() || @studentCanceled()
blurb = `<p>We're sorry this scheduling attempt did not working out for you. Please search our community of instructors to find someone else who looks like a good fit for you, and submit a new lesson request</p>`
else
blurb = `<p>We're sorry this instructor has declined your request. Please search our community of instructors to find someone else who looks like a good fit for you, and submit a new lesson request</p>`
@ -1073,11 +1084,16 @@ UserStore = context.UserStore
{blurb}
<a href="/client#/jamclass/searchOptions" className="search-for-more-teachers">SEARCH INSTRUCTORS NOW</a>
</div>`
cancelerUser = null
if this.adminCanceled()
cancelerUser = 'System'
else
cancelerUser = this.selfCanceled()
`<div className="contents">
<div className="row">
{this.userHeader(canceler)}
{action}
{this.messageBlock(this.selfCanceled(), this.state.booking.cancel_message)}
{this.messageBlock(cancelerUser, this.state.booking.cancel_message)}
</div>
{whatNow}
</div>`

View File

@ -40,7 +40,9 @@ teacherActions = window.JK.Actions.Teacher
lesson.displayStatus = 'Unconfirmed'
else if lesson.status == 'canceled'
lesson.displayStatus = 'Canceled'
if lesson.student_canceled
if lesson.canceled_by_admin
lesson.displayStatus = 'Canceled (by Admin)'
else if lesson.student_canceled
lesson.displayStatus = 'Canceled (by Student)'
else if lesson.teacher_canceled
lesson.displayStatus = 'Canceled (by Teacher)'

View File

@ -18,6 +18,7 @@
*= require easydropdown_jk
*= require ./jamkazam
*= require ./content
*= require ./toggle
*= require ./paginator
*= require ./faders
*= require ./header

View File

@ -795,4 +795,4 @@ button.stripe-connect {
vertical-align: middle;
height: 100%;
}
}
}

View File

@ -76,6 +76,18 @@
.band-actions {
margin-top:4px;
}
.teacher-search-enabled {
padding-top:8px;
margin-bottom:20px;
}
.list-search-label {
margin-right:10px;
padding-top:4px;
vertical-align:top;
display:inline-block;
}
}
.profile-head {

View File

@ -0,0 +1,59 @@
@import "client/common.scss";@charset "UTF-8";
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 24px;
}
/* Hide default HTML checkbox */
.switch input {display:none;}
/* The slider */
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 16px;
width: 25px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: $ColorScreenPrimary;
}
input:focus + .slider {
box-shadow: 0 0 1px $ColorScreenPrimary;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/* Rounded sliders */
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}

View File

@ -1,6 +1,6 @@
object @lesson_booking
attributes :id, :status, :lesson_type, :payment_style, :recurring, :teacher_id, :description, :lesson_length, :created_at, :user_id, :active, :accepter_id, :canceler_id, :cancel_message, :booked_price, :card_presumed_ok, :no_slots, :posa_card_id, :posa_card_purchased, :remaining_roll_forward_amount_in_cents
attributes :id, :status, :lesson_type, :payment_style, :recurring, :teacher_id, :description, :lesson_length, :created_at, :user_id, :active, :accepter_id, :canceler_id, :cancel_message, :booked_price, :card_presumed_ok, :no_slots, :posa_card_id, :posa_card_purchased, :remaining_roll_forward_amount_in_cents, :canceled_by_admin
child(:lesson_booking_slots => :slots) {
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, :from_package

View File

@ -4,7 +4,7 @@ object @lesson_session
:status, :student_canceled, :teacher_canceled, :student_canceled_at, :teacher_canceled_at, :student_canceled_reason,
:teacher_canceled_reason, :status, :success, :teacher_unread_messages, :student_unread_messages, :is_active?, :recurring,
:analysed, :school_on_school?, :no_school_on_school_payment?, :payment_if_school_on_school?, :teacher_id, :student_id, :pretty_scheduled_start, :scheduled_start, :teacher_short_canceled,
:best_display_time, :remaining_roll_forward_amount_in_cents
:best_display_time, :remaining_roll_forward_amount_in_cents, :canceled_by_admin
node do |lesson_session|
{

View File

@ -37,7 +37,8 @@ attributes :id,
:errors,
:profile_pct,
:school_id,
:background_check_at
:background_check_at,
:is_searchable
child :review_summary => :review_summary do

View File

@ -66,6 +66,19 @@
</div>
<div class="profile-about-right">
<div class="section teacher-search-enabled">
<div class="section-header">Availability Settings</div>
<div>
<span class="list-search-label">Let new students find me by search:</span>
<label class="switch">
<input type="checkbox">
<span class="slider"></span>
</label>
</div>
<br clear="all" />
</div>
<div class="section bio">
<div class="section-header">Bio <a href="/client#/account/profile" class="add-bio">(edit)</a></div>
<div id="biography" class="item"></div>