VRFS-4041 - side bubble for teacher help reach out
This commit is contained in:
parent
22bd49b16b
commit
0c0e22df8c
|
|
@ -30,7 +30,7 @@ module JamRuby
|
|||
|
||||
def social(options)
|
||||
mail(to: APP_CONFIG.email_social_alias,
|
||||
from: APP_CONFIG.email_generic_from,
|
||||
from: options[:from] || APP_CONFIG.email_generic_from,
|
||||
body: options[:body],
|
||||
content_type: "text/plain",
|
||||
subject: options[:subject])
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ module JamRuby
|
|||
# only show teachers with ready for session set to true
|
||||
query = query.where('teachers.ready_for_session_at IS NOT NULL')
|
||||
|
||||
if params[:onlyMySchool] && params[:onlyMySchool] != 'false' && user.school_id
|
||||
if user && params[:onlyMySchool] && params[:onlyMySchool] != 'false' && user.school_id
|
||||
query = query.where("teachers.school_id = ?", user.school_id)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -2113,19 +2113,9 @@ module JamRuby
|
|||
end
|
||||
|
||||
def has_rated_teacher(teacher)
|
||||
teacher_rating(teacher)
|
||||
end
|
||||
|
||||
def has_rated_student(student)
|
||||
Review.where(target_id: student.id).where(target_type: "JamRuby::User").count > 0
|
||||
teacher_rating(teacher).count > 0
|
||||
end
|
||||
|
||||
def teacher_rating(teacher)
|
||||
if teacher.is_a?(JamRuby::User)
|
||||
teacher = teacher.teacher
|
||||
end
|
||||
Review.where(target_id: teacher.id).where(target_type: teacher.class.to_s)
|
||||
end
|
||||
def teacher_rating(teacher)
|
||||
if teacher.is_a?(JamRuby::User)
|
||||
teacher = teacher.teacher
|
||||
|
|
@ -2134,13 +2124,21 @@ module JamRuby
|
|||
end
|
||||
|
||||
def has_rated_student(student)
|
||||
Review.where(target_id: student.id).where(target_type: "JamRuby::User").count > 0
|
||||
student_rating(student).count > 0
|
||||
end
|
||||
|
||||
def student_rating(student)
|
||||
Review.where(target_id: student.id).where(target_type: "JamRuby::User")
|
||||
end
|
||||
|
||||
def teacher_profile_url
|
||||
"#{APP_CONFIG.external_root_url}/client#/profile/teacher/#{id}"
|
||||
end
|
||||
|
||||
def profile_url
|
||||
"#{APP_CONFIG.external_root_url}/client#/profile/#{id}"
|
||||
end
|
||||
|
||||
def ratings_url
|
||||
"#{APP_CONFIG.external_root_url}/client?tile=ratings#/profile/teacher/#{id}"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -261,7 +261,11 @@
|
|||
return;
|
||||
}
|
||||
|
||||
var activate = ["jamtrack/search", "jamtrack/filter", "shoppingCart", "checkoutPayment", "checkoutOrder", "redeemComplete", "checkoutComplete", "teachers/setup/introduction", "teachers/setup/basics", "teachers/setup/experience", "teachers/setup/pricing", "account/profile", "account/profile/experience", "account/profile/interests", "account/profile/samples"]
|
||||
var activate = ["jamtrack/search", "jamtrack/filter",
|
||||
"shoppingCart", "checkoutPayment", "checkoutOrder", "redeemComplete", "checkoutComplete",
|
||||
"teachers/setup/introduction", "teachers/setup/basics", "teachers/setup/experience", "teachers/setup/pricing",
|
||||
"account/profile", "account/profile/experience", "account/profile/interests", "account/profile/samples",
|
||||
"jamclass"]
|
||||
$(document).on(context.JK.EVENTS.SCREEN_CHANGED, function(e, data) {
|
||||
|
||||
var show = false;
|
||||
|
|
|
|||
|
|
@ -186,7 +186,7 @@
|
|||
|
||||
helpBubble.showBuyNormalLesson = function($element, $offsetParent, user, callback) {
|
||||
return context.JK.onceBubble($element, 'side-buy-normal-lesson', user, {offsetParent:$offsetParent, width:260, positions:['right'], postShow: function(container) {
|
||||
var $bookNow = $('a.book-now')
|
||||
var $bookNow = container.find('a.book-now')
|
||||
$bookNow.off('click').on('click', function(e) {
|
||||
e.preventDefault()
|
||||
callback()
|
||||
|
|
@ -194,4 +194,17 @@
|
|||
})
|
||||
}})
|
||||
}
|
||||
|
||||
helpBubble.didntFindTeacher = function($element, $offsetParent, user, callback) {
|
||||
return context.JK.onceBubble($element, 'side-didnt-find-teacher', user, {offsetParent:$offsetParent, width:260, positions:['right'], postShow: function(container) {
|
||||
var $bookNow = container.find('a.post-help')
|
||||
console.log("container", $bookNow)
|
||||
$bookNow.off('click').on('click', function(e) {
|
||||
e.preventDefault()
|
||||
callback(container.find('.note').val(), container.find('.email').val(), container.find('.phonenumber').val())
|
||||
return false;
|
||||
})
|
||||
console.log("hehelllo!")
|
||||
}})
|
||||
}
|
||||
})(window, jQuery);
|
||||
|
|
@ -2491,6 +2491,17 @@
|
|||
});
|
||||
}
|
||||
|
||||
function askSearchHelp(options) {
|
||||
|
||||
return $.ajax({
|
||||
type: "POST",
|
||||
url: '/api/teachers/search_help',
|
||||
dataType: "json",
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify(options),
|
||||
})
|
||||
}
|
||||
|
||||
function initialize() {
|
||||
return self;
|
||||
}
|
||||
|
|
@ -2714,7 +2725,7 @@
|
|||
this.listTeacherDistributions = listTeacherDistributions;
|
||||
this.lessonStartTime = lessonStartTime;
|
||||
this.createReview = createReview;
|
||||
|
||||
this.askSearchHelp = askSearchHelp;
|
||||
return this;
|
||||
};
|
||||
})(window,jQuery);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@ BroadcastHolder = React.createClass(
|
|||
notification = []
|
||||
if this.state.notification
|
||||
|
||||
if this.state.notification.isLesson
|
||||
if this.state.notification.isJamClass
|
||||
result = `<JamClassPhone key="jamclassphone" />`
|
||||
else if this.state.notification.isLesson
|
||||
result = `<InLessonBroadcast key={'lesson'} lessonSession={this.state.notification}/>`
|
||||
else
|
||||
result = `<Broadcast key={this.state.notification.id} notification={this.state.notification}/>`
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
context = window
|
||||
|
||||
@JamClassPhone = React.createClass(
|
||||
{
|
||||
displayName: 'JamClassPhone',
|
||||
|
||||
render: ->
|
||||
`<div className="jamclass-phone">
|
||||
<img src="/assets/content/phone-icon.png" width="32" height="32"/>
|
||||
|
||||
<div className="callwrapper">
|
||||
<div className="call-num">
|
||||
Call
|
||||
<span className="phonenumber">
|
||||
877-37-MUSIC
|
||||
</span>
|
||||
</div>
|
||||
<div className="call-prompt">
|
||||
Have questions? Call now!
|
||||
</div>
|
||||
</div>
|
||||
</div>`
|
||||
}
|
||||
)
|
||||
|
|
@ -22,6 +22,7 @@ ProfileActions = @ProfileActions
|
|||
visible: false
|
||||
needToSearch: true
|
||||
root: null
|
||||
screen: null
|
||||
endOfList: null
|
||||
contentBodyScroller: null
|
||||
refreshing: false
|
||||
|
|
@ -36,7 +37,6 @@ ProfileActions = @ProfileActions
|
|||
@visible = true
|
||||
|
||||
afterShow: (e) ->
|
||||
@visible = false
|
||||
#@setState(TeacherSearchStore.getState())
|
||||
#if @state.results.length == 0
|
||||
# don't issue a new search every time someone comes to the screen, to preserve location from previous browsing
|
||||
|
|
@ -48,7 +48,9 @@ ProfileActions = @ProfileActions
|
|||
@needToSearch = false
|
||||
|
||||
afterHide: (e) ->
|
||||
@visible = false
|
||||
@contentBodyScroller.off('scroll')
|
||||
@hideSideBubble()
|
||||
|
||||
onTeacherSearchStore: (storeChanged) ->
|
||||
@needToSearch = true
|
||||
|
|
@ -68,10 +70,12 @@ ProfileActions = @ProfileActions
|
|||
|
||||
componentDidMount: () ->
|
||||
@root = $(@getDOMNode())
|
||||
@screen = $('#teacherSearch')
|
||||
@resultsNode = @root.find('.results')
|
||||
@endOfList = @root.find('.end-of-teacher-list')
|
||||
@contentBodyScroller = @root
|
||||
|
||||
|
||||
registerInfiniteScroll:() ->
|
||||
$scroller = @contentBodyScroller
|
||||
logger.debug("registering infinite scroll")
|
||||
|
|
@ -87,6 +91,33 @@ ProfileActions = @ProfileActions
|
|||
logger.debug("refreshing more teachers for infinite scroll")
|
||||
TeacherSearchResultsActions.nextPage()
|
||||
)
|
||||
showSideBubble: () ->
|
||||
setTimeout(
|
||||
(() => (
|
||||
if @visible
|
||||
context.JK.HelpBubbleHelper.didntFindTeacher(@screen, null, @state.user || {}, ((note, email, phone) => @postHelp(note, email, phone)))
|
||||
)), 3000)
|
||||
|
||||
|
||||
hideSideBubble: () ->
|
||||
if @screen.btOff
|
||||
@screen.btOff()
|
||||
|
||||
postHelp: (value, email, phone) ->
|
||||
|
||||
if !value? || value == ''
|
||||
@app.layout.notify({title: 'note required', text: 'Please enter something about what you are looking for.'})
|
||||
return
|
||||
if !context.JK.currentUserId? && (!email? || email == '')
|
||||
@app.layout.notify({title: 'email required', text: 'Please enter your email.'})
|
||||
return
|
||||
|
||||
rest.askSearchHelp({note: value, email: email, phone: phone}).done((response) => @postHelpDone()).fail(@app.ajaxError)
|
||||
|
||||
postHelpDone: () ->
|
||||
console.log("show notice")
|
||||
context.JK.Banner.showNotice("request received", "We got your note. We will reach back shortly!")
|
||||
@hideSideBubble()
|
||||
|
||||
componentDidUpdate: () ->
|
||||
@resultsNode.find('.teacher-bio').each((index, element) => (
|
||||
|
|
@ -99,6 +130,9 @@ ProfileActions = @ProfileActions
|
|||
|
||||
|
||||
if @visible
|
||||
if @state.currentPage > 1
|
||||
@showSideBubble()
|
||||
|
||||
if @state.next == null
|
||||
@contentBodyScroller.off('scroll')
|
||||
if @state.currentPage == 1 and @state.results.length == 0
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ rest = context.JK.Rest()
|
|||
<h2 className="original-artist">Do you own or operate a music store or school?</h2>
|
||||
<div className="clearall"/>
|
||||
</div>
|
||||
<JamClassPhone/>
|
||||
<div className="preview-and-action-box jamclass">
|
||||
<img src="/assets/landing/arrow-1-student.png" className="arrow1-jamclass" />
|
||||
<div className="preview-jamtrack-header">
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ rest = context.JK.Rest()
|
|||
<h2 className="original-artist">Do you own/operate a music school?</h2>
|
||||
<div className="clearall"/>
|
||||
</div>
|
||||
<JamClassPhone/>
|
||||
<div className="preview-and-action-box jamclass">
|
||||
<img src="/assets/landing/arrow-1-student.png" className="arrow1-jamclass" />
|
||||
<div className="preview-jamtrack-header">
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ rest = context.JK.Rest()
|
|||
<h2 className="original-artist">Finally, online music lessons<br/>that really work!</h2>
|
||||
<div className="clearall"/>
|
||||
</div>
|
||||
<JamClassPhone/>
|
||||
<div className="preview-and-action-box jamclass">
|
||||
<img src="/assets/landing/arrow-1-student.png" className="arrow1-jamclass" />
|
||||
<div className="preview-jamtrack-header">
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ rest = context.JK.Rest()
|
|||
<h2 className="original-artist">Finally, online music lessons<br/>that really work!</h2>
|
||||
<div className="clearall"/>
|
||||
</div>
|
||||
<JamClassPhone/>
|
||||
<div className="preview-and-action-box jamclass">
|
||||
<img src="/assets/landing/arrow-1-student.png" className="arrow1-jamclass" />
|
||||
<div className="preview-jamtrack-header">
|
||||
|
|
|
|||
|
|
@ -16,9 +16,11 @@ BroadcastStore = Reflux.createStore(
|
|||
broadcast: null
|
||||
currentLessonTimer: null
|
||||
teacherFault: false
|
||||
isJamClass: false
|
||||
init: ->
|
||||
this.listenTo(context.AppStore, this.onAppInit);
|
||||
this.listenTo(context.AppStore, this.onAppInit)
|
||||
this.listenTo(context.SessionStore, this.onSessionChange)
|
||||
this.listenTo(context.NavStore, this.onNavChange)
|
||||
|
||||
onAppInit: (@app) ->
|
||||
|
||||
|
|
@ -104,6 +106,12 @@ BroadcastStore = Reflux.createStore(
|
|||
if @currentLessonTimer?
|
||||
clearInterval(@currentLessonTimer)
|
||||
@currentLessonTimer = null
|
||||
|
||||
onNavChange: (nav) ->
|
||||
path = nav.screenPath.toLowerCase()
|
||||
@isJamClass = path.indexOf('jamclass') > -1 || path.indexOf('teacher') > -1
|
||||
@changed()
|
||||
|
||||
onSessionChange: (session) ->
|
||||
|
||||
@session = session
|
||||
|
|
@ -158,6 +166,8 @@ BroadcastStore = Reflux.createStore(
|
|||
this.trigger(null)
|
||||
else
|
||||
this.trigger(@currentLesson)
|
||||
else if @isJamClass
|
||||
this.trigger({isJamClass: true})
|
||||
else
|
||||
this.trigger(@broadcast)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,6 @@ rest = new context.JK.Rest()
|
|||
@changed()
|
||||
|
||||
changed:() ->
|
||||
@trigger({currentScreen: @currentScreen, currentSection: @currentSection, currentScreenName: @currentScreenName})
|
||||
@trigger({screenPath: @screenPath, currentScreen: @currentScreen, currentSection: @currentSection, currentScreenName: @currentScreenName})
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@
|
|||
context.JK.popExternalLinks($(container))
|
||||
|
||||
if (originalPostShow) {
|
||||
originalPostShow(container);
|
||||
originalPostShow($(container));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
@import "client/common";
|
||||
|
||||
body.jam, body.web, .dialog{
|
||||
//body.jam, body.web, .dialog{
|
||||
html {
|
||||
.bt-wrapper {
|
||||
font-size: 14px;
|
||||
font-family: 'Raleway', Arial, Helvetica, sans-serif;
|
||||
font-weight: 300;
|
||||
.bt-content {
|
||||
color:#cccccc;
|
||||
|
||||
|
|
@ -91,7 +95,7 @@ body.jam, body.web, .dialog{
|
|||
}
|
||||
|
||||
|
||||
.side-remaining-test-drives, .side-buy-test-drive, .side-buy-normal-lesson {
|
||||
.side-remaining-test-drives, .side-buy-test-drive, .side-buy-normal-lesson, .side-didnt-find-teacher {
|
||||
h2 {
|
||||
font-size:20px;
|
||||
color:white;
|
||||
|
|
@ -106,6 +110,40 @@ body.jam, body.web, .dialog{
|
|||
display:block;
|
||||
margin:30px auto;
|
||||
}
|
||||
.post-help {
|
||||
width:100px;
|
||||
display:block;
|
||||
margin:30px auto;
|
||||
}
|
||||
|
||||
.textarea-wrapper {
|
||||
padding: 0 1em;
|
||||
width:100%;
|
||||
@include border_box_sizing;
|
||||
}
|
||||
|
||||
textarea {
|
||||
height:70px;
|
||||
@include border_box_sizing;
|
||||
width:100%;
|
||||
font-size:14px;
|
||||
}
|
||||
|
||||
.field {
|
||||
margin: 0 1em;
|
||||
label {
|
||||
width:50px;
|
||||
display:inline-block;
|
||||
font-size:14px;
|
||||
}
|
||||
input {
|
||||
font-size:14px;
|
||||
display:inline-block;
|
||||
margin-bottom:20px;
|
||||
width:178px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -750,4 +750,30 @@ button.stripe-connect {
|
|||
|
||||
.site-nav {
|
||||
margin-bottom:10px;
|
||||
}
|
||||
|
||||
.jamclass-phone {
|
||||
background-color:#262626;
|
||||
float:right;
|
||||
height:50px;
|
||||
font-size:16px;
|
||||
img {
|
||||
display:inline-block;
|
||||
margin-right:7px;
|
||||
vertical-align:baseline;
|
||||
}
|
||||
.callwrapper {
|
||||
display:inline-block;
|
||||
}
|
||||
.call-num {
|
||||
white-space:nowrap;
|
||||
margin-bottom:4px;
|
||||
text-align:right;
|
||||
}
|
||||
.call-prompt {
|
||||
white-space:nowrap;
|
||||
}
|
||||
.phonenumber {
|
||||
font-size:20px;
|
||||
}
|
||||
}
|
||||
|
|
@ -57,6 +57,12 @@ body.web.individual_jamtrack {
|
|||
}
|
||||
}
|
||||
|
||||
.jamclass-phone {
|
||||
position: relative;
|
||||
top: -150px;
|
||||
right: 107px;
|
||||
background-color:black;
|
||||
}
|
||||
.name-and-artist {
|
||||
padding-top: 60px;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
class ApiTeachersController < ApiController
|
||||
|
||||
before_filter :api_signed_in_user, :except => [:index, :detail, :search]
|
||||
before_filter :api_signed_in_user, :except => [:index, :detail, :search, :search_help]
|
||||
before_filter :auth_teacher, :only => [:update, :delete]
|
||||
before_filter :auth_user, :only => [:create, :update]
|
||||
|
||||
|
|
@ -46,7 +46,54 @@ class ApiTeachersController < ApiController
|
|||
respond_with_model(@intent)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def search_help
|
||||
email = params[:email]
|
||||
if current_user && email.blank?
|
||||
email = current_user.email
|
||||
end
|
||||
if current_user
|
||||
subject = "#{current_user.name} wants help searching for a teacher"
|
||||
body = "#{current_user.name} (#{email}) needs help locating a teacher.\n\n"
|
||||
if params[:phone].present?
|
||||
body << "Phone Number: #{params[:phone]}\n\n"
|
||||
else
|
||||
body << "Phone Number: None Entered\n\n"
|
||||
end
|
||||
|
||||
if params[:note].present?
|
||||
body << "Here's what they wrote: \n\n\n"
|
||||
body << params[:note]
|
||||
else
|
||||
body << "...They didn't write anything..."
|
||||
end
|
||||
|
||||
body << "\n\nAdmin: #{current_user.admin_url}"
|
||||
body << "\nProfile: #{current_user.profile_url}"
|
||||
|
||||
else
|
||||
subject = "#{email} wants help searching for a teacher"
|
||||
body = "#{email} needs help locating a teacher.\n\n"
|
||||
if params[:phone].present?
|
||||
body << "Phone Number: #{params[:phone]}\n\n"
|
||||
else
|
||||
body << "Phone Number: None Entered\n\n"
|
||||
end
|
||||
|
||||
if params[:note].present?
|
||||
body << "Here's what they wrote: \n\n\n"
|
||||
body << params[:note]
|
||||
else
|
||||
body << "...They didn't write anything..."
|
||||
end
|
||||
end
|
||||
|
||||
AdminMailer.social({from: email, body: body, subject: subject}).deliver
|
||||
|
||||
render json: { success: true }, :status => 200
|
||||
end
|
||||
|
||||
private
|
||||
def auth_teacher
|
||||
@teacher = Teacher.find(params[:id])
|
||||
|
||||
|
|
@ -71,6 +118,6 @@ private
|
|||
else
|
||||
@user=current_user
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -423,4 +423,21 @@ script type="text/template" id="template-help-side-buy-normal-lesson"
|
|||
a.book-now.button-orange BOOK NOW!
|
||||
p Or call us at
|
||||
p 877-376-8742 (877-37-MUSIC)
|
||||
p And we can answer any questions and help set you up over the phone.
|
||||
p And we can answer any questions and help set you up over the phone.
|
||||
|
||||
script type="text/template" id="template-help-side-didnt-find-teacher"
|
||||
.side-didnt-find-teacher
|
||||
h2 Let us help!
|
||||
p
|
||||
| Didn't find the teacher you want? Tell us what you're looking for,
|
||||
| and our concierge team will find one or more teachers who meet your requirements in about a week. We are 100% committed to helping you connect to your ideal teacher!
|
||||
|
||||
.field
|
||||
label Email
|
||||
input.email type="text" placeholder="Email address" value="{{data.email}}"
|
||||
.field
|
||||
label Phone
|
||||
input.phonenumber type="text" placeholder="Callback number"
|
||||
.textarea-wrapper
|
||||
textarea.note placeholder="Write a note here explaining what you need in your teacher..."
|
||||
a.post-help.button-orange SEND NOTE
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
.jamclass-phone
|
||||
|
||||
= image_tag("content/phone_icon.png", {width:32, height:32})
|
||||
|
||||
.call-num
|
||||
| Call
|
||||
span.phonenumber 877-37-MUSIC
|
||||
.call-prompt
|
||||
| Have questions? Call now!
|
||||
|
|
@ -497,6 +497,7 @@ SampleApp::Application.routes.draw do
|
|||
# teachers
|
||||
match '/teachers' => 'api_teachers#index', :via => :get
|
||||
match '/teachers/detail' => 'api_teachers#detail', :via => :get, :as => 'api_teacher_detail'
|
||||
match '/teachers/search_help' => 'api_teachers#search_help', :via => :post
|
||||
match '/teachers' => 'api_teachers#create', :via => :post
|
||||
match '/teachers/:id' => 'api_teachers#update', :via => :post
|
||||
match '/teachers/:id' => 'api_teachers#delete', :via => :delete
|
||||
|
|
|
|||
Loading…
Reference in New Issue