first attempt at onboarder interface
This commit is contained in:
parent
dacfc1abd3
commit
42205d4d87
|
|
@ -8,117 +8,166 @@ ActiveAdmin.register JamRuby::User, :as => 'Users' do
|
|||
|
||||
config.sort_order = 'created_at DESC'
|
||||
|
||||
filter :email
|
||||
filter :first_name
|
||||
filter :last_name
|
||||
filter :jamuser_full_name_or_email_cont, label: 'Name Or Email', as: :string
|
||||
filter :created_at
|
||||
filter :updated_at
|
||||
|
||||
form :partial => "form"
|
||||
|
||||
show do |user|
|
||||
attributes_table do
|
||||
row :id
|
||||
row :email
|
||||
row :admin
|
||||
row :updated_at
|
||||
row :created_at
|
||||
row :musician
|
||||
row :city
|
||||
row :state
|
||||
row :country
|
||||
row :first_name
|
||||
row :last_name
|
||||
row :birth_date
|
||||
row :gender
|
||||
row :email_confirmed
|
||||
row :remember_token
|
||||
row "Session Ready" do |user|
|
||||
div do
|
||||
if user.ready_for_session_at
|
||||
span do
|
||||
'YES'
|
||||
end
|
||||
span do
|
||||
br
|
||||
end
|
||||
span do
|
||||
link_to("mark as not checked", mark_session_not_ready_admin_student_path(user.id), {confirm: "Mark as not ready for session?"})
|
||||
end
|
||||
else
|
||||
span do
|
||||
'NO'
|
||||
end
|
||||
span do
|
||||
br
|
||||
end
|
||||
span do
|
||||
link_to("mark as checked", mark_session_ready_admin_student_path(user.id), {confirm: "Mark as ready for session?"})
|
||||
end
|
||||
panel "Common" do
|
||||
attributes_table do
|
||||
row :id
|
||||
row :email
|
||||
row :admin
|
||||
row :updated_at
|
||||
row :created_at
|
||||
row :musician
|
||||
row :city
|
||||
row :state
|
||||
row :country
|
||||
row :first_name
|
||||
row :last_name
|
||||
row :birth_date
|
||||
row :gender
|
||||
row :email_confirmed
|
||||
row :remember_token
|
||||
row "Session Ready" do |user|
|
||||
div do
|
||||
if user.ready_for_session_at
|
||||
span do
|
||||
'YES'
|
||||
end
|
||||
span do
|
||||
br
|
||||
end
|
||||
span do
|
||||
link_to("mark as not checked", mark_session_not_ready_admin_student_path(user.id), {confirm: "Mark as not ready for session?"})
|
||||
end
|
||||
else
|
||||
span do
|
||||
'NO'
|
||||
end
|
||||
span do
|
||||
br
|
||||
end
|
||||
span do
|
||||
link_to("mark as checked", mark_session_ready_admin_student_path(user.id), {confirm: "Mark as ready for session?"})
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
row :jamclass_credits
|
||||
row :via_amazon
|
||||
row "Web Profile" do
|
||||
link_to "Link", "#{Rails.application.config.external_root_url}/client#/profile/#{user.id}"
|
||||
end
|
||||
row :image do user.photo_url ? image_tag(user.photo_url) : '' end
|
||||
row "Taken Lessons" do
|
||||
table_for user.taken_lessons.order('created_at desc') do
|
||||
column "View" do |lesson_session| link_to("View", lesson_session.admin_url) end
|
||||
column :created_at
|
||||
column :status
|
||||
column "Teacher" do |lesson_session|
|
||||
teacher = lesson_session.teacher
|
||||
span do
|
||||
link_to teacher.admin_name, teacher.admin_url
|
||||
end
|
||||
end
|
||||
column "Start Time" do |lesson_session|
|
||||
span do
|
||||
lesson_session.music_session.pretty_scheduled_start(true)
|
||||
end
|
||||
br
|
||||
span do
|
||||
lesson_session.music_session.scheduled_start
|
||||
end
|
||||
end
|
||||
|
||||
row :jamclass_credits
|
||||
row :via_amazon
|
||||
row "Web Profile" do
|
||||
link_to "Link", "#{Rails.application.config.external_root_url}/client#/profile/#{user.id}"
|
||||
end
|
||||
end
|
||||
row "Taught Lessons" do
|
||||
table_for user.taught_lessons.order('created_at desc') do
|
||||
column "View" do |lesson_session| link_to("View", lesson_session.admin_url) end
|
||||
column :created_at
|
||||
column :status
|
||||
column "Student" do |lesson_session|
|
||||
student = lesson_session.student
|
||||
span do
|
||||
link_to student.admin_name, student.admin_name
|
||||
end
|
||||
end
|
||||
column "Start Time" do |lesson_session|
|
||||
span do
|
||||
lesson_session.music_session.pretty_scheduled_start(true)
|
||||
end
|
||||
br
|
||||
span do
|
||||
lesson_session.music_session.scheduled_start
|
||||
end
|
||||
end
|
||||
|
||||
row :image do
|
||||
user.photo_url ? image_tag(user.photo_url) : ''
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
panel "Onboarding" do
|
||||
attributes_table do
|
||||
row :onboarding_status
|
||||
row "Support Consultant" do |user|
|
||||
if user.onboarder
|
||||
link_to "#{user.onboarder.name} (#{user.onboarder.onboarding_users.count})", user.onboarder.admin_url
|
||||
else
|
||||
end
|
||||
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
|
||||
row "Email 3", :onboarding_email_3_sent_at
|
||||
row "Email 4", :onboarding_email_4_sent_at
|
||||
row "Email 5", :onboarding_email_5_sent_at
|
||||
row "Test Session Scheduled Time", :onboarding_test_session_at
|
||||
row "When Test Session Was Requested", :onboarding_test_session_at
|
||||
row "Test Session Outcome", :onboarding_test_session_outcome
|
||||
row "Notes", :onboarding_onboarder_notes
|
||||
row "Lost Reason", :onboarding_lost_reason
|
||||
row "Lost At", :onboarding_lost_at
|
||||
row "Escalated Reason", :onboarding_escalation_reason
|
||||
row "Escalated At", :onboarding_escalated_at
|
||||
|
||||
end
|
||||
end
|
||||
panel "Lessons" do
|
||||
attributes_table do
|
||||
row "Taken Lessons" do
|
||||
table_for user.taken_lessons.order('created_at desc') do
|
||||
column "View" do |lesson_session|
|
||||
link_to("View", lesson_session.admin_url)
|
||||
end
|
||||
column :created_at
|
||||
column :status
|
||||
column "Teacher" do |lesson_session|
|
||||
teacher = lesson_session.teacher
|
||||
span do
|
||||
link_to teacher.admin_name, teacher.admin_url
|
||||
end
|
||||
end
|
||||
column "Start Time" do |lesson_session|
|
||||
span do
|
||||
lesson_session.music_session.pretty_scheduled_start(true)
|
||||
end
|
||||
br
|
||||
span do
|
||||
lesson_session.music_session.scheduled_start
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
row "Taught Lessons" do
|
||||
table_for user.taught_lessons.order('created_at desc') do
|
||||
column "View" do |lesson_session|
|
||||
link_to("View", lesson_session.admin_url)
|
||||
end
|
||||
column :created_at
|
||||
column :status
|
||||
column "Student" do |lesson_session|
|
||||
student = lesson_session.student
|
||||
span do
|
||||
link_to student.admin_name, student.admin_name
|
||||
end
|
||||
end
|
||||
column "Start Time" do |lesson_session|
|
||||
span do
|
||||
lesson_session.music_session.pretty_scheduled_start(true)
|
||||
end
|
||||
br
|
||||
span do
|
||||
lesson_session.music_session.scheduled_start
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
active_admin_comments
|
||||
end
|
||||
|
||||
index do
|
||||
# actions # use this for all view/edit/delete links
|
||||
column "Actions" do |user|
|
||||
links = ''.html_safe
|
||||
links << link_to("View", resource_path(user), :class => "member_link view_link")
|
||||
links << link_to("Edit", edit_resource_path(user), :class => "member_link edit_link")
|
||||
links
|
||||
end
|
||||
column "ID" do |user|
|
||||
link_to(truncate(user.id, {:length => 12}),
|
||||
resource_path(user),
|
||||
link_to(truncate(user.id, {:length => 12}),
|
||||
resource_path(user),
|
||||
{:title => user.id})
|
||||
end
|
||||
column "Email" do |user|
|
||||
|
|
@ -127,7 +176,9 @@ ActiveAdmin.register JamRuby::User, :as => 'Users' do
|
|||
column :admin
|
||||
column :updated_at
|
||||
column :created_at
|
||||
column :musician do |user| user.musician? ? true : false end
|
||||
column :musician do |user|
|
||||
user.musician? ? true : false
|
||||
end
|
||||
column :city
|
||||
column :state
|
||||
column :country
|
||||
|
|
@ -139,14 +190,6 @@ ActiveAdmin.register JamRuby::User, :as => 'Users' do
|
|||
column :photo_url
|
||||
column :session_settings
|
||||
column :can_invite
|
||||
|
||||
# actions # use this for all view/edit/delete links
|
||||
column "Actions" do |user|
|
||||
links = ''.html_safe
|
||||
links << link_to("View", resource_path(user), :class => "member_link view_link")
|
||||
links << link_to("Edit", edit_resource_path(user), :class => "member_link edit_link")
|
||||
links
|
||||
end
|
||||
end
|
||||
|
||||
controller do
|
||||
|
|
@ -177,7 +220,8 @@ ActiveAdmin.register JamRuby::User, :as => 'Users' do
|
|||
@user = resource
|
||||
@user.email = params[:jam_ruby_user][:email]
|
||||
@user.admin = params[:jam_ruby_user][:admin]
|
||||
@user.musician = params[:jam_ruby_user][:musician]
|
||||
@user.is_onboarder = params[:jam_ruby_user][:is_onboarder]
|
||||
@user.musician = params[:jam_ruby_user][:musician]
|
||||
@user.first_name = params[:jam_ruby_user][:first_name]
|
||||
@user.last_name = params[:jam_ruby_user][:last_name]
|
||||
@user.state = params[:jam_ruby_user][:state]
|
||||
|
|
@ -199,7 +243,7 @@ ActiveAdmin.register JamRuby::User, :as => 'Users' do
|
|||
|
||||
|
||||
if params[:jam_ruby_user][:configure_video_no_show].to_i == 1
|
||||
@user.mod_merge({User::MOD_NO_SHOW => {User::CONFIGURE_VIDEO_NOSHOW=> true}})
|
||||
@user.mod_merge({User::MOD_NO_SHOW => {User::CONFIGURE_VIDEO_NOSHOW => true}})
|
||||
else
|
||||
@user.delete_mod(User::MOD_NO_SHOW, User::CONFIGURE_VIDEO_NOSHOW)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,85 @@
|
|||
ActiveAdmin.register JamRuby::User, :as => 'CurrentlyOnboarding' do
|
||||
|
||||
menu :label => 'Currently Onboarding', :parent => 'JamClass'
|
||||
|
||||
config.sort_order = 'created_at desc'
|
||||
config.batch_actions = true
|
||||
config.per_page = 100
|
||||
config.paginate = true
|
||||
config.filters = true
|
||||
config.clear_action_items!
|
||||
batch_action :destroy, false
|
||||
|
||||
batch_action :onboarder, form: {
|
||||
support_consultant: (User.where(is_onboarder: true).includes(:onboarding_users).map {|user| ["#{user.name} (#{user.onboarding_users.length})", user.id]}).to_a.unshift(['Unassign', ''])
|
||||
} do |ids, inputs|
|
||||
onboarder = inputs[:support_consultant]
|
||||
if onboarder.blank?
|
||||
onboarder = nil
|
||||
else
|
||||
onboarder = User.find(onboarder)
|
||||
end
|
||||
|
||||
ids.each do |id|
|
||||
user = User.find(id)
|
||||
user.onboarder = onboarder
|
||||
user.onboarder_assigned_at = Date.today
|
||||
user.save
|
||||
end
|
||||
|
||||
if onboarder
|
||||
msg = 'Assigned ' + User.find(onboarder).name + " to #{ids.length} users"
|
||||
|
||||
else
|
||||
msg = "Unassigned any Support Consultant from #{ids.length} users"
|
||||
end
|
||||
|
||||
redirect_to :back, notice: msg
|
||||
end
|
||||
|
||||
filter :jamuser_full_name_or_email_cont, label: 'Name Or Email', as: :string
|
||||
filter :onboarder, as: :select, :collection => User.where(is_onboarder: true), label: 'Support Consultant'
|
||||
filter :onboarder_id_blank, :as => :boolean, label: 'Unassigned'
|
||||
filter :onboarding_escalation_reason_present, :as => :boolean, label: 'Escalated'
|
||||
scope("TestDrive/Amazon Users", default: true) { |scope| scope.joins(:posa_cards).where('posa_cards.lesson_package_type_id in (?)', LessonPackageType::AMAZON_PACKAGES + LessonPackageType::LESSON_PACKAGE_TYPES) }
|
||||
|
||||
|
||||
controller do
|
||||
active_admin_config.includes.push :onboarding_users
|
||||
end
|
||||
|
||||
index do
|
||||
selectable_column
|
||||
column "Name" do |user|
|
||||
link_to user.name, user.admin_url
|
||||
end
|
||||
column :email
|
||||
column :onboarding_status
|
||||
column "Lost Reason", :onboarding_lost_reason
|
||||
column "Escalated Reason", :onboarding_escalation_reason
|
||||
column "Support Consultant" do |user|
|
||||
if user.onboarder
|
||||
link_to "#{user.onboarder.name} (#{user.onboarder.onboarding_users.count})", user.onboarder.admin_url
|
||||
else
|
||||
end
|
||||
end
|
||||
|
||||
column "Signup" do |user|
|
||||
user.created_at.to_date
|
||||
end
|
||||
column "Assigned", :onboarder_assigned_at
|
||||
column "Email 1", :onboarding_email_1_sent_at
|
||||
column "Email 2", :onboarding_email_2_sent_at
|
||||
column "Email 3", :onboarding_email_3_sent_at
|
||||
column "Email 4", :onboarding_email_4_sent_at
|
||||
column "Email 5", :onboarding_email_5_sent_at
|
||||
column "Test Session", :onboarding_test_session_scheduled_at
|
||||
end
|
||||
|
||||
member_action :update_onboarder, :method => :post do
|
||||
resource.onboarder = params[:onboarder]
|
||||
resource.save!
|
||||
redirect_to :back
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -15,7 +15,8 @@ ActiveAdmin.register JamRuby::User, :as => 'Students' do
|
|||
filter :jamuser_full_name_or_email_cont, label: 'Name Or Email', as: :string
|
||||
filter :school, label: 'School'
|
||||
|
||||
scope("Default", default: true) { |scope| scope.where('is_a_student = true OR jamclass_credits > 0 OR ((select count(id) from lesson_bookings where lesson_bookings.user_id = users.id) > 0)').order('users.ready_for_session_at IS NULL DESC') }
|
||||
scope("TestDrive/Amazon Users", default: true) {|scope| scope.joins(:posa_cards).where('posa_cards.lesson_package_type_id in (?)', LessonPackageType::AMAZON_PACKAGES + LessonPackageType::LESSON_PACKAGE_TYPES) }
|
||||
scope("Student Or Has Credits Or Has Lesson") { |scope| scope.where('is_a_student = true OR jamclass_credits > 0 OR ((select count(id) from lesson_bookings where lesson_bookings.user_id = users.id) > 0)').order('users.ready_for_session_at IS NULL DESC') }
|
||||
|
||||
index do
|
||||
column "Name" do |user|
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
//= require active_admin/base
|
||||
//= require jquery3
|
||||
//= require jquery_ujs
|
||||
|
||||
//= require activeadmin_addons/all
|
||||
// //= require jquery-ui
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
#= require active_admin/base
|
||||
#= require jquery3
|
||||
|
|
|
|||
|
|
@ -10,5 +10,3 @@
|
|||
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
|
||||
// GO AFTER THE REQUIRES BELOW.
|
||||
//
|
||||
//= require jquery
|
||||
//= require jquery_ujs
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
= f.inputs "Details" do
|
||||
= f.input :email, label: 'Email'
|
||||
= f.input :admin
|
||||
= f.input :is_onboarder, label: 'Is Support Consultant'
|
||||
= f.input :first_name
|
||||
= f.input :last_name
|
||||
= f.input :city
|
||||
|
|
|
|||
|
|
@ -380,4 +380,5 @@ amazon_v1.sql
|
|||
sms_index_optimize.sql
|
||||
amazon_signup.sql
|
||||
age_out_sessions.sql
|
||||
alter_crash_dumps.sql
|
||||
alter_crash_dumps.sql
|
||||
onboarding.sql
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
ALTER TABLE users ADD COLUMN is_onboarder BOOLEAN NOT NULL DEFAULT FALSE;
|
||||
|
||||
ALTER TABLE users ADD COLUMN onboarding_status VARCHAR DEFAULT 'Unassigned';
|
||||
ALTER TABLE users ADD COLUMN onboarding_lost_reason VARCHAR;
|
||||
ALTER TABLE users ADD COLUMN onboarding_lost_at DATE;
|
||||
ALTER TABLE users ADD COLUMN onboarding_escalation_reason VARCHAR;
|
||||
ALTER TABLE users ADD COLUMN onboarding_escalated_at date;
|
||||
ALTER TABLE users ADD COLUMN onboarder_id VARCHAR(64) REFERENCES users ON DELETE SET NULL;
|
||||
ALTER TABLE users ADD COLUMN onboarder_assigned_at DATE;
|
||||
ALTER TABLE users ADD COLUMN onboarding_email_1_sent_at DATE;
|
||||
ALTER TABLE users ADD COLUMN onboarding_email_2_sent_at DATE;
|
||||
ALTER TABLE users ADD COLUMN onboarding_email_3_sent_at DATE;
|
||||
ALTER TABLE users ADD COLUMN onboarding_email_4_sent_at DATE;
|
||||
ALTER TABLE users ADD COLUMN onboarding_email_5_sent_at DATE;
|
||||
ALTER TABLE users ADD COLUMN onboarding_test_session_scheduled_at DATE;
|
||||
ALTER TABLE users ADD COLUMN onboarding_test_session_at timestamp without time zone;
|
||||
ALTER TABLE users ADD COLUMN onboarding_test_session_outcome VARCHAR;
|
||||
ALTER TABLE users ADD COLUMN onboarding_onboarded_at DATE;
|
||||
ALTER TABLE users ADD COLUMN onboarding_onboarder_notes VARCHAR;
|
||||
ALTER TABLE users ADD COLUMN first_onboarding_free_lesson_at timestamp without time zone;
|
||||
ALTER TABLE users ADD COLUMN first_onboarding_paid_lesson_at timestamp without time zone;
|
||||
|
||||
|
|
@ -116,6 +116,10 @@ module JamRuby
|
|||
description(lesson_booking)
|
||||
end
|
||||
|
||||
def is_free?
|
||||
# if it's a 0 dollar test drive, or the 'single free' lesson, then it's atually free
|
||||
(is_test_drive && price == 0.00) || is_single_free
|
||||
end
|
||||
def is_single_free?
|
||||
id == SINGLE_FREE
|
||||
end
|
||||
|
|
|
|||
|
|
@ -173,7 +173,7 @@ module JamRuby
|
|||
|
||||
# give 2 days to auto-cancel; this lets people just jump in a session and mark it done
|
||||
def self.auto_cancel
|
||||
MusicSession.joins(lesson_session: :lesson_booking).where('lesson_sessions.status = ? OR lesson_bookings.status = ?', LessonSession::STATUS_REQUESTED, LessonBooking::STATUS_COUNTERED).where("? > scheduled_start + (INTERVAL '2 days' * (duration))", Time.now).each do |music_session|
|
||||
MusicSession.joins(lesson_session: :lesson_booking).where('lesson_sessions.status = ? OR lesson_bookings.status = ?', LessonSession::STATUS_REQUESTED, LessonBooking::STATUS_COUNTERED).where("? > scheduled_start + (INTERVAL '2 days') + (INTERVAL '1 minutes' * (duration))", Time.now).each do |music_session|
|
||||
lesson_session = music_session.lesson_session
|
||||
lesson_session.autocancel
|
||||
end
|
||||
|
|
@ -218,6 +218,10 @@ module JamRuby
|
|||
counterer_id.nil? || counterer_id == student_id
|
||||
end
|
||||
|
||||
def lesson_is_free?
|
||||
lesson_booking.booked_price == 0.00
|
||||
end
|
||||
|
||||
def mark_lesson(success, administratively_marked = false)
|
||||
if !self.success && (is_requested? || is_unconfirmed?)
|
||||
# what's going on here is that we will just mark a session as a success if the the people got in in case of requested sessions
|
||||
|
|
@ -291,6 +295,24 @@ module JamRuby
|
|||
return
|
||||
end
|
||||
|
||||
|
||||
# record how it went
|
||||
if analysis[:student_analysis] && analysis[:student_analysis][:total_time] > 0
|
||||
if lesson_is_free?
|
||||
if student.first_onboarding_free_lesson_at.nil?
|
||||
student.first_onboarding_free_lesson_at = Time.now
|
||||
student.save
|
||||
end
|
||||
else
|
||||
if student.first_onboarding_paid_lesson_at.nil?
|
||||
student.first_onboarding_paid_lesson_at = Time.now
|
||||
student.save
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
mark_lesson(analysis[:bill])
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,38 @@ module JamRuby
|
|||
MINIMUM_AUDIO_LATENCY = 2
|
||||
MAXIMUM_AUDIO_LATENCY = 10000
|
||||
|
||||
ONBOARDING_STATUS_UNASSIGNED = "Unassigned"
|
||||
ONBOARDING_STATUS_ASSIGNED = "Assigned"
|
||||
ONBOARDING_STATUS_EMAILED = "Emailed"
|
||||
ONBOARDING_STATUS_ONBOARDED = "Onboarded"
|
||||
ONBOARDING_STATUS_LOST = "Lost"
|
||||
ONBOARDING_STATUS_ESCALATED = "Escalated"
|
||||
ONBOARDING_STATUS_FREE_LESSON = "Free Lesson Taken"
|
||||
ONBOARDING_STATUS_PAID_LESSON = "Paid Lesson Taken"
|
||||
ONBOARDING_STATUES = [ONBOARDING_STATUS_UNASSIGNED, ONBOARDING_STATUS_ASSIGNED, ONBOARDING_STATUS_EMAILED, ONBOARDING_STATUS_ONBOARDED, ONBOARDING_STATUS_LOST, ONBOARDING_STATUS_ESCALATED, ONBOARDING_STATUS_FREE_LESSON, ONBOARDING_STATUS_PAID_LESSON ]
|
||||
SESSION_OUTCOME_SUCCESSFUL = "Successful"
|
||||
SESSION_OUTCOME_SETUP_WIZARD_FAILURE = "Setup Wizard Failure"
|
||||
SESSION_OUTCOME_NO_AUDIO_STREAM = "No Audio Stream in Session"
|
||||
SESSION_OUTCOME_NO_VIDEO_STREAM = "No Video Stream In Session"
|
||||
SESSION_OUTCOME_OTHER = "Other"
|
||||
SESSION_OUTCOMES = [SESSION_OUTCOME_SUCCESSFUL, SESSION_OUTCOME_SETUP_WIZARD_FAILURE, SESSION_OUTCOME_NO_AUDIO_STREAM, SESSION_OUTCOME_NO_VIDEO_STREAM, SESSION_OUTCOME_OTHER]
|
||||
LOST_REASON_LOST_INTEREST = "Lost Interest"
|
||||
LOST_REASON_NO_COMPUTER = "No Win/Mac Computer"
|
||||
LOST_REASON_NO_BROADBAND = "No Broadband Internet"
|
||||
LOST_REASON_NO_WEBCAM = "No Webcam"
|
||||
LOST_REASON_BAD_INTERNET = "Bad Internet"
|
||||
LOST_REASON_SETUP_WIZARD_FAILURE = "Setup Wizard Failure"
|
||||
LOST_REASON_NO_AUDIO_STREAM = "No Audio Stream In Session"
|
||||
LOST_REASON_NO_VIDEO_STREAM = "No Video Stream In Session"
|
||||
LOST_REASON_OTHER = "Other"
|
||||
LOST_REASONS = [LOST_REASON_LOST_INTEREST, LOST_REASON_NO_COMPUTER, LOST_REASON_NO_BROADBAND, LOST_REASON_NO_WEBCAM, LOST_REASON_BAD_INTERNET, LOST_REASON_SETUP_WIZARD_FAILURE, LOST_REASON_NO_AUDIO_STREAM, LOST_REASON_NO_VIDEO_STREAM, LOST_REASON_OTHER]
|
||||
ESCALATION_REASON_NO_AUDIO_STREAM = "No Audio Stream In Session"
|
||||
ESCALATION_REASON_NO_VIDEO_STREAM = "No Video Stream In Session"
|
||||
ESCALATION_REASON_SETUP_WIZARD_FAILURE = "Setup Wizard Failure"
|
||||
ESCALATION_REASON_OTHER = "Other"
|
||||
ESCALATION_REASONS = [ESCALATION_REASON_NO_AUDIO_STREAM, ESCALATION_REASON_NO_VIDEO_STREAM, ESCALATION_REASON_SETUP_WIZARD_FAILURE, ESCALATION_REASON_OTHER]
|
||||
|
||||
|
||||
devise :database_authenticatable, :recoverable, :rememberable
|
||||
|
||||
acts_as_mappable
|
||||
|
|
@ -203,6 +235,8 @@ module JamRuby
|
|||
has_many :taken_lessons, :class_name => "JamRuby::LessonSession", inverse_of: :user, foreign_key: :user_id
|
||||
has_many :taught_lessons, :class_name => "JamRuby::LessonSession", inverse_of: :teacher, foreign_key: :teacher_id
|
||||
belongs_to :school, :class_name => "JamRuby::School", inverse_of: :students
|
||||
belongs_to :onboarder, :class_name => "JamRuby::User", inverse_of: :onboarding_users, foreign_key: :onboarder_id
|
||||
has_many :onboarding_users, :class_name => "JamRuby::User", inverse_of: :onboarder, foreign_key: :onboarder_id
|
||||
has_one :owned_school, :class_name => "JamRuby::School", inverse_of: :user
|
||||
has_one :owned_retailer, :class_name => "JamRuby::Retailer", inverse_of: :user
|
||||
has_many :test_drive_package_choices, :class_name =>"JamRuby::TestDrivePackageChoice"
|
||||
|
|
@ -239,6 +273,7 @@ module JamRuby
|
|||
validates :show_whats_next, :inclusion => {:in => [nil, true, false]}
|
||||
validates :is_a_student, :inclusion => {:in => [true, false]}
|
||||
validates :is_a_teacher, :inclusion => {:in => [true, false]}
|
||||
validates :is_onboarder, :inclusion => {:in => [true, false, nil]}
|
||||
#validates :mods, json: true
|
||||
validates_numericality_of :last_jam_audio_latency, greater_than: MINIMUM_AUDIO_LATENCY, less_than: MAXIMUM_AUDIO_LATENCY, :allow_nil => true
|
||||
validates :last_jam_updated_reason, :inclusion => {:in => [nil, JAM_REASON_REGISTRATION, JAM_REASON_NETWORK_TEST, JAM_REASON_FTUE, JAM_REASON_JOIN, JAM_REASON_IMPORT, JAM_REASON_LOGIN]}
|
||||
|
|
@ -287,6 +322,16 @@ module JamRuby
|
|||
retailer.save!
|
||||
end
|
||||
end
|
||||
|
||||
if onboarding_lost_reason_changed? ||
|
||||
onboarding_escalation_reason_changed? ||
|
||||
onboarder_id_changed? ||
|
||||
onboarding_email_5_sent_at_changed? ||
|
||||
first_onboarding_free_lesson_at_changed? ||
|
||||
first_onboarding_paid_lesson_at_changed? ||
|
||||
onboarding_onboarded_at
|
||||
User.where(id: self.id).update_all(onboarding_status: self.computed_onboarding_status)
|
||||
end
|
||||
end
|
||||
def update_teacher_pct
|
||||
if teacher
|
||||
|
|
@ -2006,6 +2051,10 @@ module JamRuby
|
|||
APP_CONFIG.admin_root_url + "/admin/students" # should add id; not yet supported
|
||||
end
|
||||
|
||||
def admin_onboarding_url
|
||||
APP_CONFIG.admin_root_url + "/admin/currently_onboardings"
|
||||
end
|
||||
|
||||
def jam_track_rights_admin_url
|
||||
APP_CONFIG.admin_root_url + "/admin/jam_track_rights?q[user_id_equals]=#{id}&commit=Filter&order=created_at DESC"
|
||||
end
|
||||
|
|
@ -2443,6 +2492,26 @@ module JamRuby
|
|||
student.school && self.teacher && self.teacher.school && student.school.id == self.teacher.school.id
|
||||
end
|
||||
|
||||
def computed_onboarding_status
|
||||
if first_onboarding_paid_lesson_at
|
||||
ONBOARDING_STATUS_PAID_LESSON
|
||||
elsif first_onboarding_free_lesson_at
|
||||
ONBOARDING_STATUS_FREE_LESSON
|
||||
elsif onboarding_onboarded_at
|
||||
ONBOARDING_STATUS_ONBOARDED
|
||||
elsif onboarding_lost_reason
|
||||
ONBOARDING_STATUS_LOST
|
||||
elsif onboarding_escalation_reason
|
||||
ONBOARDING_STATUS_ESCALATED
|
||||
elsif onboarding_email_5_sent_at
|
||||
ONBOARDING_STATUS_EMAILED
|
||||
elsif onboarder_id
|
||||
ONBOARDING_STATUS_ASSIGNED
|
||||
else
|
||||
ONBOARDING_STATUS_UNASSIGNED
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def create_remember_token
|
||||
self.remember_token = SecureRandom.urlsafe_base64
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@
|
|||
affiliate_referral_count: userDetail.affiliate_referral_count,
|
||||
owns_school: !!userDetail.owned_school_id,
|
||||
owns_retailer: !!userDetail.owned_retailer_id,
|
||||
is_onboarder: userDetail.is_onboarder,
|
||||
webcamName: webcamName
|
||||
} , { variable: 'data' }));
|
||||
|
||||
|
|
@ -148,6 +149,7 @@
|
|||
$("#account-content-scroller").on('click', '#account-affiliate-partner-link', function(evt) {evt.stopPropagation(); navToAffiliates(); return false; } );
|
||||
$("#account-content-scroller").on('click', '#account-school-link', function(evt) {evt.stopPropagation(); navToSchool(); return false; } );
|
||||
$("#account-content-scroller").on('click', '#account-retailer-link', function(evt) {evt.stopPropagation(); navToRetailer(); return false; } );
|
||||
$("#account-content-scroller").on('click', '#account-onboarder-link', function(evt) {evt.stopPropagation(); navToOnboarder(); return false; } );
|
||||
}
|
||||
|
||||
function renderAccount() {
|
||||
|
|
@ -215,6 +217,11 @@
|
|||
window.location = '/client#/account/retailer'
|
||||
}
|
||||
|
||||
function navToOnboarder() {
|
||||
resetForm()
|
||||
window.location = '/client#/account/onboarder'
|
||||
}
|
||||
|
||||
// handle update avatar event
|
||||
function updateAvatar(avatar_url) {
|
||||
var photoUrl = context.JK.resolveAvatarUrl(avatar_url);
|
||||
|
|
|
|||
|
|
@ -2812,6 +2812,38 @@
|
|||
})
|
||||
}
|
||||
|
||||
function listOnboardings(id) {
|
||||
return $.ajax({
|
||||
type: "GET",
|
||||
url: '/api/users/' + id + '/onboardings',
|
||||
dataType: "json",
|
||||
contentType: 'application/json'
|
||||
})
|
||||
}
|
||||
|
||||
function getOnboarding(id) {
|
||||
return $.ajax({
|
||||
type: "GET",
|
||||
url: '/api/users/' + id + '/onboardings',
|
||||
dataType: "json",
|
||||
contentType: 'application/json'
|
||||
})
|
||||
}
|
||||
function updateOnboarding(options) {
|
||||
options = options || {}
|
||||
var id = options.id
|
||||
delete options.id
|
||||
|
||||
return $.ajax({
|
||||
type: 'POST',
|
||||
url: '/api/users/' + id + '/onboardings',
|
||||
dataType: 'json',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify(options)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
function initialize() {
|
||||
return self;
|
||||
|
|
@ -3062,6 +3094,9 @@
|
|||
this.liveStreamTransition = liveStreamTransition;
|
||||
this.getLiveStream = getLiveStream;
|
||||
this.getBroadcast = getBroadcast;
|
||||
this.listOnboardings = listOnboardings;
|
||||
this.getOnboarding = getOnboarding;
|
||||
this.updateOnboarding = updateOnboarding;
|
||||
return this;
|
||||
};
|
||||
})(window, jQuery);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,424 @@
|
|||
context = window
|
||||
rest = context.JK.Rest()
|
||||
logger = context.JK.logger
|
||||
|
||||
AppStore = context.AppStore
|
||||
UserStore = context.UserStore
|
||||
|
||||
profileUtils = context.JK.ProfileUtils
|
||||
|
||||
@AccountOnboarderScreen = React.createClass({
|
||||
|
||||
mixins: [
|
||||
Reflux.listenTo(AppStore, "onAppInit"),
|
||||
Reflux.listenTo(UserStore, "onUserChanged")
|
||||
]
|
||||
|
||||
shownOnce: false
|
||||
screenVisible: false
|
||||
updates: []
|
||||
|
||||
onAppInit: (@app) ->
|
||||
@app.bindScreen('account/onboarder', {beforeShow: @beforeShow, afterShow: @afterShow, beforeHide: @beforeHide})
|
||||
|
||||
onRetailerChanged: (retailerState) ->
|
||||
@setState(retailerState)
|
||||
|
||||
onUserChanged: (userState) ->
|
||||
@getOnboardings(userState?.user)
|
||||
@setState({user: userState?.user})
|
||||
|
||||
componentDidMount: () ->
|
||||
@root = $(@getDOMNode())
|
||||
|
||||
componentDidUpdate: () ->
|
||||
datePicked = @datePicked.bind(this)
|
||||
@sessionDate = @root.find('.date-picker')
|
||||
@sessionDate.each(() ->
|
||||
$this = $(this)
|
||||
id = $this.attr('data-id')
|
||||
day = $this.attr('data-date')
|
||||
console.log("id-day: #{id}-#{day}")
|
||||
|
||||
$this.datepicker({
|
||||
dateFormat: "D M d yy",
|
||||
onSelect: ((e, inst) ->
|
||||
datePicked(id, 'onboarding_test_session_at')
|
||||
)
|
||||
})
|
||||
|
||||
# initialize day
|
||||
if(day)
|
||||
$this.datepicker("setDate", new Date(day))
|
||||
)
|
||||
|
||||
|
||||
toggleDate: (dateText, inst) ->
|
||||
console.log("THIS", this)
|
||||
console.log("IDID ID #{$(this).attr('data-id')}")
|
||||
'onboarding_test_session_at'
|
||||
|
||||
beforeHide: (e) ->
|
||||
@screenVisible = false
|
||||
return true
|
||||
|
||||
beforeShow: (e) ->
|
||||
LocationActions.load()
|
||||
|
||||
getOnboardings: (user) ->
|
||||
if user?.id? && @screenVisible
|
||||
@setState({fetchingOnboardings: true})
|
||||
rest.listOnboardings(context.JK.currentUserId).done((onboardings) =>
|
||||
@setState({onboardings: onboardings, fetchingOnboardings: false})
|
||||
)
|
||||
.fail(() =>
|
||||
@setState({fetchingOnboardings: false})
|
||||
@app.layout.notify({
|
||||
title: "onboarding list failure",
|
||||
text: "Unable to list out your onboarders. Please contact support@jamkazam.com"
|
||||
})
|
||||
)
|
||||
|
||||
afterShow: (e) ->
|
||||
@screenVisible = true
|
||||
logger.debug("AccountOnboardingScreen: afterShow")
|
||||
logger.debug("after show", @state.user)
|
||||
@getOnboardings(@state.user)
|
||||
|
||||
getInitialState: () ->
|
||||
{
|
||||
onboardings: [],
|
||||
user: null,
|
||||
updating: false
|
||||
}
|
||||
|
||||
onCancel: (e) ->
|
||||
e.preventDefault()
|
||||
context.location.href = '/client#/account'
|
||||
|
||||
onUpdate: (e) ->
|
||||
if e?
|
||||
e.preventDefault()
|
||||
|
||||
if this.state.updating
|
||||
return
|
||||
|
||||
if @updates.length == 0
|
||||
return
|
||||
update = @updates.pop()
|
||||
|
||||
@setState({updating: true})
|
||||
rest.updateOnboarding(update).done((response) => @onUpdateDone(response)).fail((jqXHR) => @onUpdateFail(jqXHR))
|
||||
|
||||
|
||||
updateRow: (onboarding) ->
|
||||
for match in this.state.onboardings
|
||||
if match.id == onboarding.id
|
||||
$.extend(match, onboarding)
|
||||
console.log("UPDATED ONBOARDING", onboarding)
|
||||
break
|
||||
this.setState({onboardings: this.state.onboardings})
|
||||
|
||||
onUpdateDone: (response) ->
|
||||
this.setState({updating: false})
|
||||
@updateRow(response)
|
||||
if @updates.length > 0
|
||||
@onUpdate()
|
||||
|
||||
onUpdateFail: (jqXHR) ->
|
||||
this.setState({updating: false})
|
||||
@app.layout.notify({
|
||||
title: "update failure",
|
||||
text: "Unable to update user. Please let us know which user and field you can not update."
|
||||
})
|
||||
if @updates.length > 0
|
||||
@onUpdate()
|
||||
|
||||
|
||||
|
||||
|
||||
updateField: (id, field, e) ->
|
||||
if(e)
|
||||
e.preventDefault()
|
||||
|
||||
console.log("Update Field Called for ", field)
|
||||
$target = $(e.target)
|
||||
console.log("TARGET", $target)
|
||||
value = true
|
||||
if $target.is('select')
|
||||
value = $target.val()
|
||||
options = {id: id}
|
||||
options[field] = value
|
||||
@queueUpdate(options)
|
||||
|
||||
queueUpdate: (update) ->
|
||||
@updates.push(update)
|
||||
@onUpdate()
|
||||
|
||||
|
||||
createLinkField: (id, onboarding, field, display, timeLabel = null) ->
|
||||
if onboarding[field]
|
||||
email = `<span style={{whiteSpace:'no-wrap'}}>{timeLabel} {onboarding[field]}</span>`
|
||||
else
|
||||
email = `<a href='#' className="daystamper" onClick={this.updateField.bind(this, id, field)}>{display}</a>`
|
||||
|
||||
email
|
||||
|
||||
showLostBanner: (id, field) ->
|
||||
lostHtml = 'Please choose a reason why the user was lost:<br/><br/>' +
|
||||
'<select><option value=""></option><option value="Lost Interest">Lost Interest</option><option value="No Win/Mac Computer">No Win/Mac Computer</option><option value="No Broadband Internet">No Broadband Internet</option><option value="No Webcam">No Webcam</option><option value="Bad Internet">Bad Internet</option><option value="Setup Wizard Failure">Setup Wizard Failure</option><option value="No Audio Stream In Session">No Audio Stream In Session</option><option value="No Video Stream In Session">No Video Stream In Session</option><option value="Other">Other</option></select>'
|
||||
$item = context.JK.Banner.showAlert(
|
||||
{buttons: [{name: 'CANCEL', click: () -> console.log('cancel clicked')}], html: lostHtml});
|
||||
$item.find('select').change((e)=> @updateField(id, field, e); context.JK.Banner.hide())
|
||||
|
||||
showEscalationBanner: (id, field) ->
|
||||
lostHtml = 'Please choose a reason why the user needs escalating:<br/><br/>' +
|
||||
'<select><option value=""></option><option value="No Audio Stream In Session">No Audio Stream In Session</option><option value="No Video Stream In Session">No Video Stream In Session</option><option value="Setup Wizard Failure">Setup Wizard Failure</option><option value="Other">Other</option></select>'
|
||||
$item = context.JK.Banner.showAlert(
|
||||
{buttons: [{name: 'CANCEL', click: () -> console.log('cancel clicked')}], html: lostHtml});
|
||||
$item.find('select').change((e)=> @updateField(id, field, e); context.JK.Banner.hide())
|
||||
|
||||
createLostField: (id, onboarding) ->
|
||||
field = 'onboarding_lost_reason'
|
||||
if onboarding[field]
|
||||
field = `<span>{
|
||||
onboarding[field]
|
||||
}</span>`
|
||||
else
|
||||
field = `<a href='#' className="daystamper" onClick={this.showLostBanner.bind(this, id, field)}>lost student</a>`
|
||||
field
|
||||
|
||||
createEscalatedField: (id, onboarding) ->
|
||||
field = 'onboarding_escalation_reason'
|
||||
if onboarding[field]
|
||||
field = `<span>{
|
||||
onboarding[field]
|
||||
}</span>`
|
||||
else
|
||||
field = `<a href='#' className="daystamper" onClick={this.showEscalationBanner.bind(this, id, field)}>escalate
|
||||
student</a>`
|
||||
field
|
||||
|
||||
createOnboardingField: (onboarding) ->
|
||||
@createLinkField(onboarding.id, onboarding, 'onboarding_onboarded_at', 'onboarded successfully')
|
||||
|
||||
watchTextArea: (id, field, e) ->
|
||||
$text = $(e.target)
|
||||
if @textTimeout
|
||||
clearTimeout(@textTimeout)
|
||||
@textTimeout = null
|
||||
@textTimeout = setTimeout(() =>
|
||||
data = {id: id}
|
||||
data[field] = $text.val()
|
||||
@queueUpdate(data)
|
||||
@textTimeout = null
|
||||
, 3000)
|
||||
|
||||
datePicked: (id, field) ->
|
||||
$tr = @root.find("tr[data-id='" + id + "']")
|
||||
picker = $tr.find(".date-picker")
|
||||
|
||||
hour = $tr.find('.hour').val()
|
||||
minute = $tr.find('.minute').val()
|
||||
am_pm = $tr.find('.am_pm').val()
|
||||
|
||||
|
||||
if hour? and hour != ''
|
||||
hour = new Number(hour)
|
||||
if am_pm == 'PM'
|
||||
hour += 12
|
||||
else
|
||||
hour = null
|
||||
|
||||
if minute? and minute != ''
|
||||
minute = new Number(minute)
|
||||
else
|
||||
minute = null
|
||||
|
||||
date = picker.datepicker("getDate")
|
||||
if date?
|
||||
date.setHours(hour)
|
||||
date.setMinutes(minute)
|
||||
data = {id: id}
|
||||
data[field] = date.toString()
|
||||
@queueUpdate(data)
|
||||
|
||||
mainContent: () ->
|
||||
cancelClasses = {"button-orange": true, "update": true}
|
||||
onboardings = []
|
||||
|
||||
if @state.fetchingOnboardings
|
||||
onboardings.push(`<tr>
|
||||
<td colSpan="6" style={{textAlign:'center'}}>FETCHING STUDENTS</td>
|
||||
</tr>`)
|
||||
else if @state.onboardings.length > 0
|
||||
for onboarding in @state.onboardings
|
||||
console.log("onboarding", onboarding)
|
||||
|
||||
hours = []
|
||||
for hour in ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12']
|
||||
if hour == '12'
|
||||
key = '00'
|
||||
else
|
||||
key = hour
|
||||
hours.push(`<option key={key} value={key}>{hour}</option>`)
|
||||
|
||||
minutes = []
|
||||
for minute in ['00', '15', '30', '45']
|
||||
minutes.push(`<option key={minute} value={minute}>{minute}</option>`)
|
||||
|
||||
am_pm = [`<option key="AM" value="AM">AM</option>`, `<option key="PM" value="PM">PM</option>`]
|
||||
|
||||
|
||||
email1 = @createLinkField(onboarding.id, onboarding, 'onboarding_email_1_sent_at', 'sent email 1', 'email 1:')
|
||||
email2 = @createLinkField(onboarding.id, onboarding, 'onboarding_email_2_sent_at', 'sent email 2', 'email 2:')
|
||||
email3 = @createLinkField(onboarding.id, onboarding, 'onboarding_email_3_sent_at', 'sent email 3', 'email 3:')
|
||||
email4 = @createLinkField(onboarding.id, onboarding, 'onboarding_email_4_sent_at', 'sent email 4', 'email 4:')
|
||||
email5 = @createLinkField(onboarding.id, onboarding, 'onboarding_email_5_sent_at', 'sent email 5', 'email 5:')
|
||||
|
||||
scheduledSession = @createLinkField(onboarding.id, onboarding, 'onboarding_test_session_scheduled_at',
|
||||
'scheduled session', 'scheduled session:')
|
||||
|
||||
sessionOutcomes = ['', "Successful", "Setup Wizard Failure", "No Audio Stream in Session",
|
||||
"No Video Stream In Session", "Other"]
|
||||
sessionOutcomesJSX = []
|
||||
for sessionOutcome in sessionOutcomes
|
||||
sessionOutcomesJSX.push(`<option key={sessionOutcome} value={sessionOutcome}>{sessionOutcome}</option>`)
|
||||
selectSessionOutcome =
|
||||
`<select value={onboarding.onboarding_test_session_outcome}
|
||||
onChange={this.updateField.bind(this, onboarding.id, 'onboarding_test_session_outcome')}>{sessionOutcomesJSX}</select>`
|
||||
|
||||
session_at = onboarding.onboarding_test_session_at
|
||||
amOrPm = 'AM'
|
||||
currentHours = '01'
|
||||
currentMinutes = '00'
|
||||
if session_at
|
||||
sessionTime = new Date(session_at)
|
||||
session_at = sessionTime.toLocaleDateString()
|
||||
currentHours = sessionTime.getHours()
|
||||
if sessionTime.getHours() > 11
|
||||
amOrPm = 'PM'
|
||||
currentHours -= 12
|
||||
|
||||
currentMinutes = sessionTime.getMinutes()
|
||||
|
||||
active = onboarding.onboarding_status == 'Emailed' || onboarding.onboarding_status == 'Assigned'
|
||||
|
||||
if active
|
||||
activeClass = 'active-row'
|
||||
else
|
||||
activeClass = 'inactive-row'
|
||||
|
||||
columns = []
|
||||
if active
|
||||
columns.push(`<td key="1">
|
||||
<div className="info-unit">{email1}</div>
|
||||
<div className="info-unit">{email2}</div>
|
||||
<div className="info-unit">{email3}</div>
|
||||
<div className="info-unit">{email4}</div>
|
||||
<div className="info-unit">{email5}</div>
|
||||
</td>`)
|
||||
columns.push(`<td key="2">
|
||||
<div className="info-unit" style={{textAlign:'center'}}>{scheduledSession}</div>
|
||||
|
||||
<div className="info-unit">
|
||||
<label style={{marginBottom:'5px'}}>Test Session Date:</label> <input data-id={onboarding.id}
|
||||
className="date-picker"
|
||||
name="session-date" type="text"
|
||||
data-date={session_at}></input>
|
||||
<label style={{marginTop:'10px'}}>Test Session Time:</label>
|
||||
<select value={currentHours}
|
||||
onChange={this.datePicked.bind(this, onboarding.id, 'onboarding_test_session_at')}
|
||||
className="hour">{hours}</select> : <select value={currentMinutes}
|
||||
onChange={this.datePicked.bind(this, onboarding.id, 'onboarding_test_session_at')}
|
||||
className="minute">{minutes}</select>
|
||||
<select value={amOrPm} style={{marginLeft:'13px'}}
|
||||
onChange={this.datePicked.bind(this, onboarding.id, 'onboarding_test_session_at')}
|
||||
className="am_pm">{am_pm}</select>
|
||||
</div>
|
||||
<div className="info-unit"><label style={{marginTop:'10px'}}>Session Outcome:</label>
|
||||
{selectSessionOutcome}</div>
|
||||
</td>`)
|
||||
columns.push(`<td key="3">
|
||||
<div className="info-unit"
|
||||
style={{marginBottom:'20px'}}>{this.createLostField(onboarding.id, onboarding)}</div>
|
||||
<div className="info-unit">{this.createEscalatedField(onboarding.id, onboarding)}</div>
|
||||
</td>`)
|
||||
columns.push(`<td key="4">
|
||||
<div className="info-unit">{this.createOnboardingField(onboarding)}</div>
|
||||
</td>`)
|
||||
else
|
||||
columns.push(`<td colSpan="4" style={{textAlign:'center', fontSize:'18px'}}>
|
||||
<span>{onboarding.onboarding_status}</span><br/><br/><span style={{fontSize:'12px', marginLeft:'10px'}}>This student will drop off your list now next time you visit this page.</span>
|
||||
</td>`)
|
||||
onboarding = `<tr className={activeClass} key={onboarding.id} data-id={onboarding.id}>
|
||||
<td>
|
||||
<div className="info-unit">Name: {onboarding.name}</div>
|
||||
<div className="info-unit">Email: {onboarding.email}</div>
|
||||
<div className="info-unit" style={{marginTop:'20px'}}>Assigned
|
||||
on {onboarding.onboarder_assigned_at}</div>
|
||||
</td>
|
||||
{columns}
|
||||
<td>
|
||||
<div className="info-unit">
|
||||
<textarea className="info-unit"
|
||||
onChange={this.watchTextArea.bind(this, onboarding.id, 'onboarding_onboarder_notes')}>{onboarding.onboarding_onboarder_notes}</textarea>
|
||||
</div>
|
||||
</td>
|
||||
</tr>`
|
||||
onboardings.push(onboarding)
|
||||
else
|
||||
onboardings.push(`<tr>
|
||||
<td colSpan="6" style={{textAlign:'center'}}>NO STUDENTS ASSIGNED</td>
|
||||
</tr>`)
|
||||
|
||||
`<div className="account-block info-block">
|
||||
<div>
|
||||
<div className="instructions">
|
||||
Manage the users here that you are responsible for onboarding.<br/><br/>
|
||||
All links in the table below, when clicked, will immediately mark that item as done. For example, once
|
||||
you send your first email to the user, click the <b>sent email 1</b> link.<br/><br/>
|
||||
All other fields will automatically save as you use them, such as the <b>Session Outcome</b> dropdown,
|
||||
and the <b>Notes</b> text box.
|
||||
</div>
|
||||
<table className="jamtable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>User</th>
|
||||
<th>Email</th>
|
||||
<th>Test Session</th>
|
||||
<th>Problems</th>
|
||||
<th>Onboarded</th>
|
||||
<th>Notes</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{onboardings}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="actions">
|
||||
<a className={classNames(cancelClasses)} onClick={this.onCancel}>BACK</a>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
render: () ->
|
||||
mainContent = @mainContent()
|
||||
|
||||
`<div className="content-body-scroller">
|
||||
<div className="profile-header profile-head">
|
||||
<div className="clearall"></div>
|
||||
</div>
|
||||
|
||||
<div className="profile-body">
|
||||
<div className="profile-wrapper">
|
||||
<div className="main-content">
|
||||
{mainContent}
|
||||
<br />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`
|
||||
})
|
||||
|
|
@ -167,9 +167,10 @@ ProfileActions = @ProfileActions
|
|||
window.location.href = '/client#/jamclass/test-drive-selection/' + user.id
|
||||
else if response.remaining_test_drives > 0
|
||||
if response.booked_with_teacher && !context.JK.currentUserAdmin
|
||||
logger.debug("TeacherSearchScreen: teacher already test-drived")
|
||||
|
||||
context.JK.Banner.showAlert('TestDrive', "You have already taken a TestDrive lesson from this teacher. With TestDrive, you need to use your lessons on 4 different teachers to find one who is best for you. We're sorry, but you cannot take multiple TestDrive lessons from a single teacher.")
|
||||
#logger.debug("TeacherSearchScreen: teacher already test-drived")
|
||||
#context.JK.Banner.showAlert('TestDrive', "You have already taken a TestDrive lesson from this teacher. With TestDrive, you need to use your lessons on 4 different teachers to find one who is best for you. We're sorry, but you cannot take multiple TestDrive lessons from a single teacher.")
|
||||
logger.debug("TeacherSearchScreen: user being sent to book a lesson")
|
||||
window.location.href = '/client#/jamclass/book-lesson/test-drive_' + user.id
|
||||
else
|
||||
# send on to booking screen for this teacher
|
||||
logger.debug("TeacherSearchScreen: user being sent to book a lesson")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,316 @@
|
|||
@import "client/common";
|
||||
|
||||
|
||||
#account-onboarder {
|
||||
div[data-react-class="AccountOnboarderScreen"] {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.daystamper {
|
||||
white-space: nowrap;
|
||||
color:#fc0;
|
||||
}
|
||||
.profile-header {
|
||||
padding: 10px 30px !important;
|
||||
}
|
||||
.info-unit {
|
||||
margin:5px 10px;
|
||||
font-size:12px;
|
||||
|
||||
select {font-size:12px; margin-top:5px;}
|
||||
textarea{font-size:12px;min-height:100px;min-width:200px;}
|
||||
|
||||
}
|
||||
.instructions {
|
||||
margin-bottom:20px;
|
||||
}
|
||||
td {
|
||||
vertical-align:middle;
|
||||
}
|
||||
td > * {
|
||||
vertical-align : middle;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
min-width: 200px;
|
||||
}
|
||||
input {
|
||||
min-width:200px;
|
||||
}
|
||||
.hint {
|
||||
margin-left: 200px;
|
||||
font-size: 12px;
|
||||
font-style: italic;
|
||||
margin-top: 5px;
|
||||
}
|
||||
.iradio_minimal {
|
||||
display: inline-block;
|
||||
top: 4px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.field {
|
||||
margin-bottom: 30px;
|
||||
|
||||
&.stripe-connect {
|
||||
margin-bottom: 10px;
|
||||
label {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.usage-hint {
|
||||
font-size:12px;
|
||||
text-decoration:underline;
|
||||
margin-left:10px;
|
||||
}
|
||||
.scooter {
|
||||
margin-bottom:10px;
|
||||
}
|
||||
div.retailer-split {
|
||||
margin-top:10px;
|
||||
}
|
||||
.store-header {
|
||||
float: left;
|
||||
padding-top: 10px;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.profile-nav a {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
height: 100%;
|
||||
width: 98%;
|
||||
margin: 0 auto;
|
||||
padding: 11px 0 0 0;
|
||||
@include border-box_sizing;
|
||||
}
|
||||
|
||||
.profile-tile {
|
||||
width: 25%;
|
||||
float: left;
|
||||
@include border-box_sizing;
|
||||
height: 40px;
|
||||
position: relative;
|
||||
}
|
||||
.profile-body {
|
||||
padding-top: 40px;
|
||||
}
|
||||
.profile-photo {
|
||||
width: 16%;
|
||||
@include border-box_sizing;
|
||||
}
|
||||
.profile-nav {
|
||||
margin: 0;
|
||||
width: 84%;
|
||||
}
|
||||
.profile-wrapper {
|
||||
padding: 10px 20px
|
||||
}
|
||||
|
||||
.main-content {
|
||||
float: left;
|
||||
@include border-box_sizing;
|
||||
width: 84%;
|
||||
}
|
||||
|
||||
.info-block {
|
||||
min-height:400px;
|
||||
h3 {
|
||||
font-weight: normal;
|
||||
font-size: 14px;
|
||||
margin-bottom: 10px;
|
||||
min-width: 200px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-bottom: 40px;
|
||||
&.teachers {
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
table.jamtable {
|
||||
font-size: 12px;
|
||||
width: 100%;
|
||||
}
|
||||
table.jamtable tr.inactive-row {
|
||||
background-color:gray;
|
||||
}
|
||||
}
|
||||
|
||||
.stripe-connect {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
outline:transparent;
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
.actions {
|
||||
float: left;
|
||||
margin-top: 30px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
a.cancel {
|
||||
margin-left:3px;
|
||||
}
|
||||
|
||||
|
||||
.avatar-edit-link {
|
||||
display:inline-block;
|
||||
img {
|
||||
max-width:200px;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-edit-link {
|
||||
.hint {
|
||||
margin-left:0;
|
||||
}
|
||||
}
|
||||
|
||||
.column {
|
||||
width:50%;
|
||||
@include border_box_sizing;
|
||||
|
||||
h3 {
|
||||
float:left;
|
||||
}
|
||||
.invite-dialog {
|
||||
float:right;
|
||||
margin-right:2px;
|
||||
}
|
||||
&.column-left {
|
||||
float:left;
|
||||
padding-right:30px;
|
||||
|
||||
}
|
||||
&.column-right {
|
||||
float:right;
|
||||
padding-left:30px;
|
||||
}
|
||||
|
||||
.username {
|
||||
max-width:40%;
|
||||
font-size:16px;
|
||||
color:white;
|
||||
}
|
||||
table {
|
||||
width:100%;
|
||||
}
|
||||
td.description {
|
||||
font-size:16px;
|
||||
color: white;
|
||||
vertical-align: top;
|
||||
white-space: nowrap;
|
||||
}
|
||||
td.message {
|
||||
color: $ColorTextTypical;
|
||||
padding-left: 10px;
|
||||
vertical-align: top;
|
||||
text-align:right;
|
||||
}
|
||||
.detail-block {
|
||||
display:inline-block;
|
||||
font-size:12px;
|
||||
}
|
||||
.resend {
|
||||
float:left;
|
||||
}
|
||||
.delete {
|
||||
float:right;
|
||||
}
|
||||
.teacher-invites, .student-invites {
|
||||
margin-bottom: 20px;
|
||||
margin-top:40px;
|
||||
font-size:12px;
|
||||
min-height:40px;
|
||||
p {
|
||||
font-size:12px;
|
||||
}
|
||||
}
|
||||
.teachers, .students {
|
||||
margin-bottom:20px;
|
||||
}
|
||||
p {
|
||||
font-size:12px;
|
||||
margin-left:0;
|
||||
}
|
||||
.retailer-invitation {
|
||||
margin-bottom:20px;
|
||||
}
|
||||
}
|
||||
.retailer-user {
|
||||
margin-bottom:20px;
|
||||
|
||||
.avatar {
|
||||
position:absolute;
|
||||
padding:1px;
|
||||
width:32px;
|
||||
height:32px;
|
||||
background-color:#ed4818;
|
||||
margin:0;
|
||||
-webkit-border-radius:16px;
|
||||
-moz-border-radius:16px;
|
||||
border-radius:16px;
|
||||
float:none;
|
||||
}
|
||||
.avatar img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
-webkit-border-radius:16px;
|
||||
-moz-border-radius:16px;
|
||||
border-radius:16px;
|
||||
}
|
||||
|
||||
.usersname {
|
||||
margin-left:56px;
|
||||
line-height:32px;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
|
||||
}
|
||||
|
||||
select[name="regions"] {
|
||||
margin-botom:30px;
|
||||
}
|
||||
|
||||
select[name="cities"] {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.just-name {
|
||||
display:block;
|
||||
}
|
||||
.just-email {
|
||||
position: relative;
|
||||
top: -14px;
|
||||
font-size:12px;
|
||||
}
|
||||
|
||||
.user-actions {
|
||||
float: right;
|
||||
line-height: 32px;
|
||||
height: 32px;
|
||||
vertical-align: middle;
|
||||
font-size:12px;
|
||||
}
|
||||
}
|
||||
p {
|
||||
font-size:12px;
|
||||
margin:0;
|
||||
}
|
||||
|
||||
.split-input {
|
||||
:after {
|
||||
content: '%';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
require 'sanitize'
|
||||
class ApiUsersController < ApiController
|
||||
|
||||
before_filter :api_signed_in_user, :except => [:create, :calendar, :show, :signup_confirm, :auth_session_create, :complete, :finalize_update_email, :isp_scoring, :add_play, :crash_dump, :validate_data, :google_auth, :user_event]
|
||||
before_filter :api_signed_in_user, :except => [:create, :calendar, :show, :signup_confirm, :auth_session_create, :complete, :finalize_update_email, :isp_scoring, :add_play, :crash_dump, :validate_data, :google_auth, :user_event, :onboardings, :update_onboarding, :show_onboarding]
|
||||
before_filter :auth_user, :only => [:session_settings_show, :session_history_index, :session_user_history_index, :update, :delete, :authorizations, :test_drive_status,
|
||||
:liking_create, :liking_destroy, # likes
|
||||
:following_create, :following_show, :following_destroy, # followings
|
||||
|
|
@ -1090,6 +1090,40 @@ class ApiUsersController < ApiController
|
|||
@teacher = User.find(params[:teacher_id])
|
||||
end
|
||||
|
||||
def onboardings
|
||||
@onboardings = current_user.onboarding_users.where('onboarding_status in (?)', [User::ONBOARDING_STATUS_ASSIGNED, User::ONBOARDING_STATUS_EMAILED]).order(:onboarder_assigned_at)
|
||||
end
|
||||
|
||||
def update_onboarding
|
||||
user = User.find(params[:id])
|
||||
if params[:onboarding_lost_reason]
|
||||
user.onboarding_lost_reason = params[:onboarding_lost_reason]
|
||||
user.onboarding_lost_at = Date.today
|
||||
end
|
||||
if params[:onboarding_escalation_reason]
|
||||
user.onboarding_escalation_reason = params[:onboarding_escalation_reason]
|
||||
user.onboarding_escalated_at = Date.today
|
||||
end
|
||||
|
||||
user.onboarding_email_1_sent_at = Date.today if params[:onboarding_email_1_sent_at]
|
||||
user.onboarding_email_2_sent_at = Date.today if params[:onboarding_email_2_sent_at]
|
||||
user.onboarding_email_3_sent_at = Date.today if params[:onboarding_email_3_sent_at]
|
||||
user.onboarding_email_4_sent_at = Date.today if params[:onboarding_email_4_sent_at]
|
||||
user.onboarding_email_5_sent_at = Date.today if params[:onboarding_email_5_sent_at]
|
||||
user.onboarding_test_session_scheduled_at = Date.today if params[:onboarding_test_session_scheduled_at]
|
||||
user.onboarding_test_session_at = params[:onboarding_test_session_at] if params[:onboarding_test_session_at]
|
||||
user.onboarding_test_session_outcome = params[:onboarding_test_session_outcome] if params[:onboarding_test_session_outcome]
|
||||
user.onboarding_onboarded_at = Date.today if params[:onboarding_onboarded_at]
|
||||
user.onboarding_onboarder_notes = params[:onboarding_onboarder_notes] if params[:onboarding_onboarder_notes]
|
||||
user.save
|
||||
user.reload
|
||||
@onboarding = user
|
||||
end
|
||||
|
||||
def show_onboarding
|
||||
@onboarding = User.find(params[:id])
|
||||
end
|
||||
|
||||
###################### RECORDINGS #######################
|
||||
# def recording_index
|
||||
# @recordings = User.recording_index(current_user, params[:id])
|
||||
|
|
|
|||
|
|
@ -569,7 +569,7 @@ class LandingsController < ApplicationController
|
|||
|
||||
body = "Name: #{@current_user.name}\n"
|
||||
body << "Email: #{@current_user.email}\n"
|
||||
body << "Admin: #{@current_user.admin_student_url}\n"
|
||||
body << "Admin: #{@current_user.admin_onboarding_url}\n"
|
||||
body << "Package Details: \n"
|
||||
body << " Package: #{card.lesson_package_type.id}\n"
|
||||
body << " Credits: #{card.credits}\n"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
object @onboardings
|
||||
|
||||
extends "api_users/show_onboarding"
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
object @user
|
||||
|
||||
attributes :id, :first_name, :last_name, :name, :city, :state, :country, :location, :online, :photo_url, :musician, :gender, :birth_date, :internet_service_provider, :friend_count, :liker_count, :like_count, :follower_count, :following_count,
|
||||
:recording_count, :session_count, :biography, :favorite_count, :audio_latency, :upcoming_session_count, :age, :website, :skill_level, :reuse_card, :email_needs_verification, :is_a_teacher, :is_a_student
|
||||
:recording_count, :session_count, :biography, :favorite_count, :audio_latency, :upcoming_session_count, :age, :website, :skill_level, :reuse_card, :email_needs_verification, :is_a_teacher, :is_a_student, :is_onboarder
|
||||
|
||||
node :location do |user|
|
||||
if user.musician?
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
object @onboarding
|
||||
|
||||
attributes :id, :name, :email, :onboarding_status, :onboarder_assigned_at, :onboarding_email_1_sent_at, :onboarding_email_2_sent_at, :onboarding_email_3_sent_at, :onboarding_email_4_sent_at, :onboarding_email_5_sent_at, :onboarding_test_session_scheduled_at, :onboarding_test_session_outcome, :onboarding_test_session_at, :onboarding_onboarded_at, :onboarding_onboarder_notes, :onboarding_lost_reason, :onboarding_lost_at, :onboarding_escalation_reason, :onboarding_escalated_at
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
object @onboarding
|
||||
|
||||
extends "api_users/show_onboarding"
|
||||
|
|
@ -181,6 +181,22 @@
|
|||
<br clear="all" />
|
||||
{% } %}
|
||||
|
||||
{% if (data.is_onboarder) { %}
|
||||
<hr />
|
||||
<div class="account-left">
|
||||
<h2>onboarding:</h2>
|
||||
</div>
|
||||
<div class="account-mid school">
|
||||
<div class="whitespace">
|
||||
<span class="school-info">Manage the JamKazam users that you are assigned to for onboarding.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right">
|
||||
<a id="account-onboarder-link" href="#" class="button-orange">UPDATE</a>
|
||||
</div>
|
||||
<br clear="all" />
|
||||
{% } %}
|
||||
|
||||
{% if (data.owns_school) { %}
|
||||
<hr />
|
||||
<div class="account-left">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
#account-onboarder.screen.secondary layout="screen" layout-id="account/onboarder"
|
||||
.content-head
|
||||
.content-icon
|
||||
= image_tag "content/icon_account.png", :size => "27x20"
|
||||
h1
|
||||
| jamclass
|
||||
= render "screen_navigation"
|
||||
.content-body
|
||||
= react_component 'AccountOnboarderScreen', {}
|
||||
|
|
@ -88,6 +88,7 @@
|
|||
<%= render "account_payment_history" %>
|
||||
<%= render "account_school" %>
|
||||
<%= render "account_retailer" %>
|
||||
<%= render "account_onboarder" %>
|
||||
<%= render "inviteMusicians" %>
|
||||
<%= render "hoverBand" %>
|
||||
<%= render "hoverFan" %>
|
||||
|
|
|
|||
|
|
@ -462,6 +462,11 @@ Rails.application.routes.draw do
|
|||
match '/users/progression/social_promoted' => 'api_users#social_promoted', :via => :post
|
||||
match '/users/progression/opened_jamtrack_web_player' => 'api_users#opened_jamtrack_web_player', :via => :post
|
||||
|
||||
# onboarding
|
||||
match '/users/:id/onboardings' => 'api_users#onboardings', :via => :get
|
||||
match '/users/:id/onboardings' => 'api_users#update_onboarding', :via => :post
|
||||
match '/users/:id/onboardings' => 'api_users#show_onboarding', :via => :get
|
||||
|
||||
# events
|
||||
match '/users/event/record' => 'api_users#user_event', :via => :post
|
||||
|
||||
|
|
|
|||
|
|
@ -240,5 +240,136 @@ describe "Test Drive", :js => true, :type => :feature, :capybara_feature => true
|
|||
teacher_distribution.distributed.should be false
|
||||
|
||||
end
|
||||
|
||||
it "same teacher with posa card succeeds" do
|
||||
|
||||
PosaCard.activate(card_lessons, retailer)
|
||||
card_lessons.reload
|
||||
card_lessons.claim(user)
|
||||
card_lessons.errors.any?.should be false
|
||||
|
||||
|
||||
visit "/client#/teachers/search"
|
||||
|
||||
#Timecop.travel(Date.new(2016, 04, 01))
|
||||
|
||||
find('.teacher-search-result[data-teacher-id="' + teacher_user.id + '"] .try-test-drive').trigger(:click)
|
||||
|
||||
# no longer true
|
||||
# TryTestDriveDialog shows
|
||||
#find('.purchase-testdrive-now').trigger(:click)
|
||||
|
||||
# dismiss banner
|
||||
|
||||
fill_out_single_lesson(Time.now.to_date + 1, Time.now.to_date + 2)
|
||||
|
||||
find('#banner h1', text: 'Lesson Requested')
|
||||
find('a.button-orange', text:'CLOSE').trigger(:click)
|
||||
|
||||
|
||||
# we tell user they have test drive purchased, and take them to the teacher screen
|
||||
#find('#banner h1', text: 'TestDrive Purchased')
|
||||
#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
|
||||
#find('a.button-orange', text:'CLOSE').trigger(:click)
|
||||
|
||||
# validate that we made a test drive purchase
|
||||
lesson_package_purchase = LessonPackagePurchase.where(user_id: user.id).first
|
||||
lesson_package_purchase.should_not be_nil
|
||||
lesson_package_purchase.lesson_package_type.is_test_drive?.should be true
|
||||
lesson_package_purchase.posa_card.should eql card_lessons
|
||||
lesson_package_purchase.lesson_payment_charge.should be_nil
|
||||
user.reload
|
||||
user.remaining_test_drives.should eql 0
|
||||
user.jamclass_credits.should eql 3
|
||||
#lesson_package_purchase.amount_charged.should eql 49.99
|
||||
user.sales.count.should eql 1
|
||||
sale = user.sales.first
|
||||
sale.order_total.should eql 49.99
|
||||
sale.recurly_total_in_cents.should be_nil
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
user.reload
|
||||
user.onboarding_status.should eql User::ONBOARDING_STATUS_UNASSIGNED
|
||||
user.student_lesson_bookings.count.should eql 1
|
||||
lesson_booking = user.student_lesson_bookings.first
|
||||
lesson_booking.is_requested?.should be true
|
||||
user.remaining_test_drives.should eql 0
|
||||
lesson_booking.lesson_sessions.count.should eql 1
|
||||
|
||||
lesson_session1 = lesson_booking.lesson_sessions.first
|
||||
# approve by teacher:
|
||||
teacher_approve(lesson_session1)
|
||||
successful_lesson(lesson_session1)
|
||||
|
||||
# then log back in as student
|
||||
switch_user(user, "/client#/teachers/search")
|
||||
|
||||
|
||||
user.most_recent_test_drive_purchase.should_not be_nil
|
||||
# let's make sure we can ask for another test drive too!
|
||||
teacher_user.teacher.ready_for_session_at = Time.now
|
||||
teacher_user.teacher.save!
|
||||
|
||||
find('a.teacher-search-options').trigger(:click)
|
||||
find('a.search-btn').trigger(:click)
|
||||
|
||||
find('.teacher-search-result[data-teacher-id="' + teacher_user.id + '"] .try-test-drive').trigger(:click)
|
||||
|
||||
find('h2', text: 'book testdrive lesson')
|
||||
find('.booking-info', text: '3 TestDrive lesson credits')
|
||||
|
||||
fill_out_single_lesson(Time.now.to_date + 1, Time.now.to_date + 2)
|
||||
|
||||
# we tell user they have test drive purchased, and take them to the teacher screen
|
||||
find('#banner h1', text: 'Lesson Requested')
|
||||
# dismiss banner
|
||||
find('a.button-orange', text:'CLOSE').trigger(:click)
|
||||
|
||||
user.student_lesson_bookings.count.should eql 2
|
||||
lesson_booking2 = user.student_lesson_bookings.order(:created_at).last
|
||||
lesson_booking2.teacher.should eql teacher_user
|
||||
lesson_session2 = lesson_booking2.lesson_sessions[0]
|
||||
lesson_session2.lesson_package_purchase.should_not be_nil
|
||||
|
||||
# approve by teacher:
|
||||
teacher_approve(lesson_session2)
|
||||
|
||||
successful_lesson(lesson_session2)
|
||||
|
||||
LessonSession.hourly_check
|
||||
lesson_session1.reload
|
||||
lesson_session1.analysed.should be true
|
||||
analysis = lesson_session1.analysis
|
||||
analysis["reason"].should eql LessonSessionAnalyser::SUCCESS
|
||||
lesson_session1.billing_attempts.should be_nil
|
||||
lesson_session1.billed.should eql false
|
||||
lesson_session1.success.should be true
|
||||
|
||||
|
||||
LessonBooking.hourly_check
|
||||
|
||||
lesson_session1.reload
|
||||
teacher_distribution = lesson_session1.teacher_distribution
|
||||
teacher_distribution.amount_in_cents.should eql 1000
|
||||
teacher_distribution.ready.should be true
|
||||
teacher_distribution.distributed.should be false
|
||||
|
||||
lesson_session2.reload
|
||||
teacher_distribution = lesson_session2.teacher_distribution
|
||||
teacher_distribution.amount_in_cents.should eql 1000
|
||||
teacher_distribution.ready.should be true
|
||||
teacher_distribution.distributed.should be false
|
||||
|
||||
user.reload
|
||||
user.first_onboarding_paid_lesson_at.should_not be_nil
|
||||
user.onboarding_status.should eql User::ONBOARDING_STATUS_PAID_LESSON
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -22,7 +22,8 @@ def failed_lesson(lesson_session, advance_to_end = true)
|
|||
end
|
||||
def teacher_approve(lesson_session)
|
||||
|
||||
sign_out_poltergeist(validate: true)
|
||||
#sign_out_poltergeist(validate: true)
|
||||
sign_out
|
||||
sign_in_poltergeist(lesson_session.teacher, password: 'foobar')
|
||||
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)
|
||||
|
|
@ -35,21 +36,24 @@ end
|
|||
def date_picker_format(date)
|
||||
date.strftime('%a %b %d %Y')
|
||||
end
|
||||
def fill_out_single_lesson
|
||||
def fill_out_single_lesson(first_date = Date.new(2016, 4, 17), second_date = Date.new(2016, 4, 18))
|
||||
|
||||
first = first_date.strftime('%a %d %b %Y')
|
||||
second = second_date.strftime('%a %d %b %Y')
|
||||
|
||||
find('h2', text: 'book testdrive lesson')
|
||||
find('.booking-info', text: 'If you need to cancel')
|
||||
|
||||
# book the lesson
|
||||
fill_in "slot-1-date", with: "Sun Apr 17 2016"
|
||||
fill_in "slot-1-date", with: first# "Sun Apr 17 2016"
|
||||
#find('.slot.slot-1 input.hasDatepicker').trigger(:click)
|
||||
# click 4-6
|
||||
find('td a', text: '17').trigger(:click)
|
||||
find('td a', text: first_date.day).trigger(:click)
|
||||
|
||||
#find('.slot.slot-2 input.hasDatepicker').trigger(:click)
|
||||
# click 4-7
|
||||
fill_in "slot-2-date", with: "Mon Apr 18 2016"
|
||||
find('td a', text: '18').trigger(:click)
|
||||
fill_in "slot-2-date", with: second #"Mon Apr 18 2016"
|
||||
find('td a', text: second_date.day).trigger(:click)
|
||||
|
||||
fill_in 'user-description', with: 'abc def dog neck'
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue