diff --git a/ruby/lib/jam_ruby/models/language.rb b/ruby/lib/jam_ruby/models/language.rb index bb5d64316..ae249ff8e 100644 --- a/ruby/lib/jam_ruby/models/language.rb +++ b/ruby/lib/jam_ruby/models/language.rb @@ -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 diff --git a/ruby/lib/jam_ruby/models/teacher.rb b/ruby/lib/jam_ruby/models/teacher.rb index 5d0f583f9..5135b91e4 100644 --- a/ruby/lib/jam_ruby/models/teacher.rb +++ b/ruby/lib/jam_ruby/models/teacher.rb @@ -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) diff --git a/ruby/spec/jam_ruby/models/language_spec.rb b/ruby/spec/jam_ruby/models/language_spec.rb new file mode 100644 index 000000000..ae1961cf1 --- /dev/null +++ b/ruby/spec/jam_ruby/models/language_spec.rb @@ -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 diff --git a/web/app/assets/javascripts/accounts_profile.js b/web/app/assets/javascripts/accounts_profile.js index e9a874de8..d2c325343 100644 --- a/web/app/assets/javascripts/accounts_profile.js +++ b/web/app/assets/javascripts/accounts_profile.js @@ -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) }); diff --git a/web/app/assets/javascripts/globals.js b/web/app/assets/javascripts/globals.js index b67879cba..bde47b613 100644 --- a/web/app/assets/javascripts/globals.js +++ b/web/app/assets/javascripts/globals.js @@ -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 = { diff --git a/web/app/assets/javascripts/helpBubbleHelper.js b/web/app/assets/javascripts/helpBubbleHelper.js index f9419b189..11de7b8cb 100644 --- a/web/app/assets/javascripts/helpBubbleHelper.js +++ b/web/app/assets/javascripts/helpBubbleHelper.js @@ -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); \ No newline at end of file diff --git a/web/app/assets/javascripts/react-components/TeacherExperienceEditableList.js.jsx.coffee b/web/app/assets/javascripts/react-components/TeacherExperienceEditableList.js.jsx.coffee index 28b9f2378..f21101986 100644 --- a/web/app/assets/javascripts/react-components/TeacherExperienceEditableList.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/TeacherExperienceEditableList.js.jsx.coffee @@ -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 ` - ` + ` dtLabel = "Start & End" else dtLabel = "Date" @@ -99,7 +102,7 @@ logger = context.JK.logger - +
diff --git a/web/app/assets/javascripts/react-components/TeacherProfile.js.jsx.coffee b/web/app/assets/javascripts/react-components/TeacherProfile.js.jsx.coffee index 533b3539a..b916c9136 100644 --- a/web/app/assets/javascripts/react-components/TeacherProfile.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/TeacherProfile.js.jsx.coffee @@ -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, diff --git a/web/app/assets/javascripts/react-components/TeacherSetupIntroduction.js.jsx.coffee b/web/app/assets/javascripts/react-components/TeacherSetupIntroduction.js.jsx.coffee index 79b9b8f7a..74ccc15f3 100644 --- a/web/app/assets/javascripts/react-components/TeacherSetupIntroduction.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/TeacherSetupIntroduction.js.jsx.coffee @@ -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" diff --git a/web/app/assets/javascripts/react-components/TeacherSetupPricing.js.jsx.coffee b/web/app/assets/javascripts/react-components/TeacherSetupPricing.js.jsx.coffee index 57ec7ebc6..5178559e5 100644 --- a/web/app/assets/javascripts/react-components/TeacherSetupPricing.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/TeacherSetupPricing.js.jsx.coffee @@ -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.

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.

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.

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.

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() TestDrive lessons per week
- 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. learn more about TestDrive + 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.
diff --git a/web/app/assets/javascripts/react-components/YearSelect.js.jsx.coffee b/web/app/assets/javascripts/react-components/YearSelect.js.jsx.coffee index 9b543533e..b9f2ec61c 100644 --- a/web/app/assets/javascripts/react-components/YearSelect.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/YearSelect.js.jsx.coffee @@ -9,10 +9,16 @@ logger = context.JK.logger options = [] now = new Date().getFullYear() + options.push `` for yr in [now..1916] options.push `` - ` {options} ` }) \ No newline at end of file diff --git a/web/app/assets/javascripts/react-components/mixins/TeacherSetupMixin.js.coffee b/web/app/assets/javascripts/react-components/mixins/TeacherSetupMixin.js.coffee index 38904f992..ecdfd5d54 100644 --- a/web/app/assets/javascripts/react-components/mixins/TeacherSetupMixin.js.coffee +++ b/web/app/assets/javascripts/react-components/mixins/TeacherSetupMixin.js.coffee @@ -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() diff --git a/web/app/assets/javascripts/utils.js b/web/app/assets/javascripts/utils.js index 22eb158fd..87dd50dbc 100644 --- a/web/app/assets/javascripts/utils.js +++ b/web/app/assets/javascripts/utils.js @@ -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) { diff --git a/web/app/assets/stylesheets/client/help.css.scss b/web/app/assets/stylesheets/client/help.css.scss index cf239ca8b..f29cd263e 100644 --- a/web/app/assets/stylesheets/client/help.css.scss +++ b/web/app/assets/stylesheets/client/help.css.scss @@ -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 { diff --git a/web/app/assets/stylesheets/client/teachers.css.scss b/web/app/assets/stylesheets/client/teachers.css.scss index a0b3b1652..102933ef3 100644 --- a/web/app/assets/stylesheets/client/teachers.css.scss +++ b/web/app/assets/stylesheets/client/teachers.css.scss @@ -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; } diff --git a/web/app/controllers/api_jamblasters_controller.rb b/web/app/controllers/api_jamblasters_controller.rb index 27c1f7a5e..69b6d7b91 100644 --- a/web/app/controllers/api_jamblasters_controller.rb +++ b/web/app/controllers/api_jamblasters_controller.rb @@ -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 diff --git a/web/app/controllers/api_languages_controller.rb b/web/app/controllers/api_languages_controller.rb index 82042444a..eb7b866c7 100644 --- a/web/app/controllers/api_languages_controller.rb +++ b/web/app/controllers/api_languages_controller.rb @@ -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 diff --git a/web/app/views/api_users/profile_show.rabl b/web/app/views/api_users/profile_show.rabl index cf8a4c4d5..b2698d1c4 100644 --- a/web/app/views/api_users/profile_show.rabl +++ b/web/app/views/api_users/profile_show.rabl @@ -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 diff --git a/web/app/views/clients/_help.html.slim b/web/app/views/clients/_help.html.slim index 143a643dd..ad30e8b0c 100644 --- a/web/app/views/clients/_help.html.slim +++ b/web/app/views/clients/_help.html.slim @@ -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 - |  for a help article that explains how to fill out your musician profile. \ No newline at end of file + |  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 + |   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. \ No newline at end of file diff --git a/web/app/views/clients/teachers/setup/_introduction.html.slim b/web/app/views/clients/teachers/setup/_introduction.html.slim index 3756ff637..baf29e64a 100644 --- a/web/app/views/clients/teachers/setup/_introduction.html.slim +++ b/web/app/views/clients/teachers/setup/_introduction.html.slim @@ -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' \ No newline at end of file