teacher profile improvements

This commit is contained in:
Seth Call 2016-04-30 22:12:25 -05:00
parent 4abc2f81ff
commit 2e7e4dccf9
20 changed files with 195 additions and 48 deletions

View File

@ -3,5 +3,10 @@ module JamRuby
include HtmlSanitize
html_sanitize strict: [:name, :description]
has_and_belongs_to_many :teachers, :class_name => "JamRuby::Teacher", :join_table => "teachers_languages"
def self.english_sort
languages = Language.order(:description)
languages.sort_by { |l| [ l.id == 'EN' ? 0 : 1, l.description] }
end
end
end

View File

@ -55,7 +55,7 @@ module JamRuby
query = User.joins(:teacher)
# only show teachers with background check set and ready for session set to true
# only show teachers with ready for session set to true
query = query.where('teachers.ready_for_session_at IS NOT NULL')
instruments = params[:instruments]
@ -194,6 +194,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.test_drives_per_week ||= 10 # default to 10 in absence of others
teacher.school_id = params[:school_id] if params.key?(:school_id)

View File

@ -0,0 +1,11 @@
require 'spec_helper'
describe Language do
it "english_sort" do
sorted= Language.english_sort
sorted[0].id.should eql 'EN'
sorted[1].id.should eql 'AF'
sorted[-1].id.should eql 'XH'
end
end

View File

@ -7,6 +7,7 @@
var $document = $(document);
var logger = context.JK.logger;
var EVENTS = context.JK.EVENTS;
var NAMED_MESSAGES = context.JK.NAMED_MESSAGES;
var api = context.JK.Rest();
var userId;
var user = {};
@ -44,12 +45,6 @@
}
resetForm();
renderAccountProfile();
setTimeout(function() {
var $header = $('#account-edit-profile-form h2')
console.log("header ", $header, $screen)
context.JK.HelpBubbleHelper.teacherMusicianProfile($header, $screen);
}, 2000)
}
function resetForm() {
@ -235,13 +230,23 @@
enableSubmits();
}
function teacherGuidance() {
if(recentUserDetail && recentUserDetail.is_a_teacher) {
setTimeout(function() {
var $header = $('#account-edit-profile-form h2')
context.JK.HelpBubbleHelper.teacherMusicianProfile($header, $screen);
}, 2000)
}
}
function renderAccountProfile() {
$.when(api.getUserProfile())
.done(function (userDetail) {
recentUserDetail = userDetail;
populateAccountProfile(userDetail);
teacherGuidance();
selectLocation = new context.JK.SelectLocation(getCountryElement(), getRegionElement(), getCityElement(), app);
selectLocation.load(userDetail.country, userDetail.state, userDetail.city)
});

View File

@ -346,7 +346,8 @@
context.JK.NAMED_MESSAGES = {
MASTER_VS_PERSONAL_MIX : 'master_vs_personal_mix',
HOWTO_USE_VIDEO_NOSHOW : 'how-to-use-video',
CONFIGURE_VIDEO_NOSHOW : 'configure-video'
CONFIGURE_VIDEO_NOSHOW : 'configure-video',
TEACHER_MUSICIAN_PROFILE : 'teacher-musician-profile'
}
context.JK.ChannelGroupIds = {

View File

@ -154,7 +154,28 @@
}
helpBubble.teacherMusicianProfile = function($element, $offsetParent) {
return context.JK.prodBubble($element, 'teacher-musician-profile', {}, bigHelpDarkOptions({spikeGirth:0, spikeLength: 0, duration:60000, offsetParent:$offsetParent, width:385, positions:['top', 'right', 'bottom']}))
return context.JK.prodBubble($element, 'teacher-musician-profile', {}, bigHelpDarkOptions({spikeGirth:0, spikeLength: 0, duration:10000, offsetParent:$offsetParent, width:385, positions:['top', 'right', 'bottom']}))
}
helpBubble.teacherProfile = function($element, $offsetParent) {
return context.JK.prodBubble($element, 'teacher-profile', {}, bigHelpDarkOptions({spikeGirth:0, spikeLength: 0, duration:10000, offsetParent:$offsetParent, width:385, positions:['top', 'right', 'bottom']}))
}
helpBubble.showUseRemainingTestDrives = function($element, $offsetParent) {
return context.JK.onceBubble($element, 'side-remaining-test-drives', {}, {offsetParent:$offsetParent, width:260, positions:['right'], postShow: function(container) {
}})
}
helpBubble.showBuyTestDrive = function($element, $offsetParent) {
return context.JK.onceBubble($element, 'side-buy-test-drive', {}, {offsetParent:$offsetParent, width:260, positions:['right'], postShow: function(container) {
}})
}
helpBubble.showBuyNormalLesson = function($element, $offsetParent) {
return context.JK.onceBubble($element, 'side-buy-normal-lesson', {}, {offsetParent:$offsetParent, width:260, positions:['right'], postShow: function(container) {
}})
}
})(window, jQuery);

View File

@ -8,14 +8,17 @@ logger = context.JK.logger
componentDidMount: () ->
@root = jQuery(this.getDOMNode())
@root.off("submit", ".teacher-experience-teaching-form").on("submit", ".teacher-experience-teaching-form", @addExperience)
@root.off("submit", ".teacher-experience-teaching-form")#.on("submit", ".teacher-experience-teaching-form", @addExperience)
formatListItem: (obj) ->
if obj.end_year?
endYear = obj.end_year
if endYear == '0' || endYear == 0
endYear = 'Now'
else
endYear = 'Present'
endYear = 'Now'
t = "#{obj.name}/#{obj.organization} (#{obj.start_year}"
t += "-#{endYear}" if this.props.showEndDate
@ -30,12 +33,13 @@ logger = context.JK.logger
addExperience: (e) ->
e.preventDefault()
$form = e.target
$form = @root.find('.teacher-experience-teaching-form')
start_year = $("[name='start_year']", $form).val()
end_year = $("[name='end_year']", $form).val()
start_year = parseInt($("[name='start_year']", $form).val())
end_year = parseInt($("[name='end_year']", $form).val())
if this.props.showEndDate && start_year > end_year
console.log("end_yeaor", end_year)
if this.props.showEndDate && end_year != 0 && start_year > end_year
this.setState({errors: ["End year must be greater than start year"]})
else
this.props.listItems.push {
@ -48,7 +52,6 @@ logger = context.JK.logger
this.props.onItemChanged(this.props.experienceType, this.props.listItems)
#$form.reset()
this.setState({errors: null})
false
getInitialState: () ->
{errors:null}
@ -61,7 +64,7 @@ logger = context.JK.logger
endDate = []
if this.props.showEndDate
endDate.push `<span key={"end_date"} ><label htmlFor="end-year">to</label>
<YearSelect name="end_year"></YearSelect></span>`
<YearSelect name="end_year" defaultPresent={true}></YearSelect></span>`
dtLabel = "Start & End"
else
dtLabel = "Date"
@ -99,7 +102,7 @@ logger = context.JK.logger
</span>
</div>
</div>
<button className="add-experience-btn button-grey right" type="submit">ADD</button>
<button className="add-experience-btn button-grey right" type="submit" onClick={this.addExperience}>ADD</button>
</form>
<EditableList objectName={this.props.experienceType} onItemChanged={this.onItemChanged} listItems={listItems} formatListItem={this.formatListItem}/>
<div className={errorClasses}>

View File

@ -40,6 +40,7 @@ proficiencyDescriptionMap = {
TILE_SAMPLES: 'samples'
TILE_RATINGS: 'ratings'
TILE_PRICES: 'prices'
visible: false
TILES: ['about', 'experience', 'samples', 'ratings', 'prices']
@ -60,6 +61,7 @@ proficiencyDescriptionMap = {
componentDidMount: () ->
@root = $(@getDOMNode())
@screen = $('#teacher-profile')
@starbox()
componentDidUpdate:() ->
@ -91,14 +93,21 @@ proficiencyDescriptionMap = {
else
logger.debug("ignoring userDetailDone", response.id, @state.userId)
#if @visible
# @showSideBubble()
beforeHide: (e) ->
@visible = false
#@hideSideBubble();
logger.debug("TeacherProfile: beforeHide")
ProfileActions.viewTeacherProfileDone()
beforeShow: (e) ->
logger.debug("TeacherProfile: beforeShow")
afterShow: (e) ->
@visible = true
logger.debug("TeacherProfile: afterShow")
@setState({userId: e.id, user: null})
rest.getUserDetail({
@ -107,6 +116,33 @@ proficiencyDescriptionMap = {
show_profile: true
}).done((response) => @userDetailDone(response)).fail(@app.ajaxError)
showSideBubble: () ->
# :remaining_test_drives, :can_buy_test_drive?
if @user['remaining_test_drives'] > 0
@showUseRemainingTestDrivesBubble()
else if @user['can_buy_test_drive?']
@showBuyTestDriveBubble()
else
@showBuyNormalLessonBubble()
hideSideBubble: () ->
if @screen.btOff
@screen.btOff()
showUseRemainingTestDrivesBubble: ( ) ->
console.log("Ok showUseRemainingTestDrivesBubble")
context.JK.HelpBubbleHelper.showUseRemainingTestDrives(@screen, @screen)
showBuyTestDriveBubble: () ->
console.log("ok showBuyTestDriveBubble")
context.JK.HelpBubbleHelper.showBuyTestDrive(@screen, @screen)
showBuyNormalLessonBubble: () ->
console.log("OK showBuyNormalLessonBubble")
context.JK.HelpBubbleHelper.showBuyNormalLesson(@screen, @screen)
getInitialState: () ->
{
userId: null,

View File

@ -10,6 +10,14 @@ rest = window.JK.Rest()
Reflux.listenTo(TeacherStore, "onTeacherStateChanged")
]
myBeforeShow: () ->
setTimeout((() =>
$screen = @root.closest('.screen')
$header = $screen.find('h2.edit-teacher-header')
context.JK.HelpBubbleHelper.teacherProfile($header, $screen);
), 2000)
screenName: () ->
"introduction"

View File

@ -135,7 +135,7 @@ rest = window.JK.Rest()
# prevent any action if the user has unselected teach test drive...
if !this.state.teaches_test_drive
@setState({teaches_test_drive: true})
context.JK.Banner.showAlert('Test Drive Participation Required', "In order to participate in the JamClass online music lesson marketplace by JamKazam, you must be willing to teach at least 2 TestDrive classes per week, ideally more if you want to attract more new students. <br/><br/>TestDrive is the primary means by which JamKazam connects new students to teachers, so if you don't do this, the marketplace will really not help you. <br /><br />If you feel that you have a compelling reason not to give TestDrive lessons, but still want to participate in our marketplace, then please send us an email at support@jamkazam.com to chat with us about it.")
context.JK.Banner.showAlert('Test Drive Participation Required', "In order to participate in the JamClass online music lesson marketplace by JamKazam, you must be willing to teach at least 10 TestDrive classes per week, ideally more if you want to attract more new students. <br/><br/>TestDrive is the primary means by which JamKazam connects new students to teachers, so if you don't do this, the marketplace will really not help you. <br /><br />If you feel that you have a compelling reason not to give TestDrive lessons, but still want to participate in our marketplace, then please send us an email at support@jamkazam.com to chat with us about it.")
navTo = 'rejected'
else
@ -317,10 +317,7 @@ rest = window.JK.Rest()
<select name="test_drives_per_week" className="test_drives_per_week" value={this.state.test_drives_per_week} onChange={this.handleTestDriveCountChange}>{test_drive_lessons}</select>
TestDrive lessons per week</label>
<div className="test-drive-explain">
TestDrive is the primary marketing program JamKazam uses to drive new students through our marketplace to teachers.
You will be paid $10 per 30-minute TestDrive lesson that you teach. Each time you teach a TestDrive lesson, it is with a student
who has an interest in taking online music lessons through the JamClass service, so you have a solid chance to convert the TestDrive
lesson into a long-term teacher-student relationship. <a className="learn-more-about-test-drive" onClick={this.handleLearnMoreAboutTestDrive}>learn more about TestDrive</a>
TestDrive is the primary marketing program JamKazam uses to drive new students through our marketplace to teachers. You will be paid $10 per 30-minute TestDrive lesson that you teach. Teach more TestDrive lessons to acquire more students.
</div>
</div>
</div>

View File

@ -9,10 +9,16 @@ logger = context.JK.logger
options = []
now = new Date().getFullYear()
options.push `<option key="now" value="0">Now</option>`
for yr in [now..1916]
options.push `<option key={yr} value={yr}>{yr}</option>`
`<select className="YearSelect react-component" name={this.props.name} required placeholder="Select" defaultValue={now}>
if this.props?.defaultPresent
defaultValue = '0'
else
defaultValue = now
`<select className="YearSelect react-component" name={this.props.name} required placeholder="Select" defaultValue={defaultValue}>
{options}
</select>`
})

View File

@ -17,13 +17,8 @@ teacherActions = window.JK.Actions.Teacher
else
teacherActions.load.trigger({})
# TODO: Determine who started us and store, so
# we can return there in case of cancel, or being
# done. For now, teacherSetupSource() will return
# a default location:
@postmark = null
# params = this.getParams()
# @postmark = params.p
if @myBeforeShow?
@myBeforeShow()
handleErrors: (changes) ->
$(".error-text", @root).remove()

View File

@ -118,6 +118,16 @@
context.JK.helpBubble($element, templateName, data, options)
}
/** shows up once. Up to you to call btOff() when appropriate */
context.JK.onceBubble = function ($element, templateName, data, options) {
if(!options) options = {};
options['trigger'] = 'none';
options['clickAnywhereToClose'] = false
options['closeWhenOthersOpen']= false
context.JK.helpBubble($element, templateName, data, options)
$element.btOn()
}
/**
* Associates a help bubble on hover (by default) with the specified $element, using jquery.bt.js (BeautyTips)
* @param $element The element that should show the help when hovered
@ -133,12 +143,14 @@
options = {};
}
options.postShow = function(container) {
context.JK.popExternalLinks($(container))
}
if(options.persist) {
var timeout = null;
options.postShow = function(container) {
context.JK.popExternalLinks($(container))
}
$element.hoverIntent({
over: function() {
@ -176,7 +188,6 @@
holder = holder.append(helpText).html()
}
console.log("hoverBubble: " + options.duration)
context.JK.hoverBubble($element, holder, options);
}
@ -214,7 +225,6 @@
options['clickAnywhereToClose'] = false
if(!options['duration']) options['duration'] = 6000;
console.log("options: " + options.duration)
var existingTimer = $element.data("prodTimer");
if(existingTimer) {
clearTimeout(existingTimer);
@ -1154,8 +1164,9 @@
if(!$parent) $parent = $('body');
// Allow any a link with a rel="external" attribute to launch
// the link in the default browser, using jamClient:
$parent.off('click');
$parent.on('click', 'a[rel="external"]', popOpenBrowser);
var $links = $parent.find('a[rel="external"]')
$links.off('click');
$links.on('click', popOpenBrowser);
}
context.JK.popExternalLink = function (href) {

View File

@ -91,6 +91,23 @@ body.jam, body.web, .dialog{
}
.side-remaining-test-drives, .side-buy-test-drive, .side-buy-normal-lesson {
h2 {
font-size:20px;
color:white;
margin:.8em .8em;
}
p {
margin:.8em 1em;
}
.book-now {
width:100px;
display:block;
margin:30px auto;
}
}
.help-hover-recorded-tracks, .help-hover-stream-mix, .help-hover-recorded-backing-tracks {

View File

@ -317,7 +317,7 @@
}
}
select {
width:auto !important;
width:70px !important;
}
}
input, select, textarea {
@ -378,7 +378,7 @@
.teacher-field.teaches_test_drive_container {
select {
margin:0 7px;
margin:0 20px;
width:auto;
display:inline-block;
}

View File

@ -38,7 +38,7 @@ class ApiJamblastersController < ApiController
end
end
def is_allowed
def is_allowed
#Pass the jbid & cbid. Reply is no error on true, else error
jbid = params[:jbid]
user_id = params[:user_id]
@ -47,7 +47,7 @@ class ApiJamblastersController < ApiController
user = User.find_by_id(user_id)
if user.nil?
render :json => {:message => "No user associated with the user #{user_id}", reason: "user_id"}, :status => 404
render :json => {:message => "No usepar associated with the user #{user_id}", reason: "user_id"}, :status => 404
return
end

View File

@ -3,8 +3,8 @@ class ApiLanguagesController < ApiController
respond_to :json
def index
@languages = Language.order(:description)
@languages = @languages.sort_by { |l| l.id == 'EN' ? 0 : 1 }
@languages = Language.english_sort
respond_with @languages
end

View File

@ -1,7 +1,7 @@
object @profile
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, :concert_count, :studio_session_count, :virtual_band, :virtual_band_commitment, :traditional_band, :traditional_band_commitment, :traditional_band_touring, :paid_sessions, :paid_sessions_hourly_rate,
:paid_sessions_daily_rate, :free_sessions, :cowriting, :cowriting_purpose, :subscribe_email
:paid_sessions_daily_rate, :free_sessions, :cowriting, :cowriting_purpose, :subscribe_email, :is_a_teacher, :is_a_student
child :online_presences => :online_presences do
attributes :id, :service_type, :username

View File

@ -392,4 +392,34 @@ script type="text/template" id="template-help-teacher-musician-profile"
.teacher-musician-profile.big-dark-help
p
a href="https://jamkazam.desk.com/customer/en/portal/articles/2405835-creating-your-teacher-profile#EditProfile" rel="external" Click here
| &nbsp;for a help article that explains how to fill out your musician profile.
| &nbsp;for a help article that explains how to fill out your musician profile.
script type="text/template" id="template-help-teacher-profile"
.teacher-profile.big-dark-help
p
a href="https://jamkazam.desk.com/customer/en/portal/articles/2405835-creating-your-teacher-profile#EditTeacherProfile" rel="external" Click here
| &nbsp; for a help article that explains how to fill out your teacher profile effectively to attract students.
script type="text/template" id="template-help-side-remaining-test-drives"
.side-remaining-test-drives
p You currently have {{data.remaining_test_drives}} TestDrive lesson credits available.
a.book-now.button-orange BOOK NOW!
script type="text/template" id="template-help-side-buy-test-drive"
.side-buy-test-drive
h2 Book TestDrive Lesson
p TestDrive is the best way to get started with lessons. We offer packages to let you try lessons with 2 or 4 different teachers at highly discounted rates to find the teacher who is best for you. Or if you're confident you've found the right one, you can get a discounted rate on your first lesson to get rolling.
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.
script type="text/template" id="template-help-side-buy-normal-lesson"
.side-buy-normal-lesson
h2 Book Lesson
p Lessons with {{data.first_name}} start at just ${{data.cheapest_lesson_stmt}}.
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.

View File

@ -9,5 +9,5 @@
.content-body-scroller
.teacher-setup-form
.teacher-setup-step-0.teacher-step.content-wrapper
h2 edit teacher profile: introduction
h2.edit-teacher-header edit teacher profile: introduction
=react_component 'TeacherSetupIntroduction'