From 920d648a2b4ae40be4d899f2e218c439ecc68201 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Tue, 31 May 2016 19:20:03 -0500 Subject: [PATCH] slew of jamclass fixes --- admin/app/admin/user_source.rb | 2 + ruby/lib/jam_ruby.rb | 5 +- ruby/lib/jam_ruby/models/genre.rb | 3 +- ruby/lib/jam_ruby/models/instrument.rb | 3 +- ruby/lib/jam_ruby/models/language.rb | 3 +- .../jam_ruby/models/lesson_booking_slot.rb | 3 + ruby/lib/jam_ruby/models/lesson_session.rb | 20 +- ruby/lib/jam_ruby/models/subject.rb | 3 +- ruby/lib/jam_ruby/models/teacher.rb | 16 +- ruby/lib/jam_ruby/models/teacher_genre.rb | 11 + .../lib/jam_ruby/models/teacher_instrument.rb | 11 + ruby/lib/jam_ruby/models/teacher_language.rb | 11 + ruby/lib/jam_ruby/models/teacher_subject.rb | 11 + ruby/lib/jam_ruby/models/user.rb | 2 +- .../jam_ruby/models/lesson_session_spec.rb | 51 ++++- ruby/spec/jam_ruby/models/teacher_spec.rb | 16 +- .../resque/google_analytics_event_spec.rb | 4 + ruby/spec/spec_helper.rb | 2 +- .../assets/javascripts/helpBubbleHelper.js | 4 +- .../JamClassSearchHeader.js.jsx.coffee | 135 +++++++++++++ .../TeacherProfile.js.jsx.coffee | 26 ++- .../TeacherSearchScreen.js.jsx.coffee | 188 ++++-------------- ...lassStudentLandingBottomPage.js.jsx.coffee | 4 +- .../TeacherSearchResultsStore.js.coffee | 12 +- .../wizard/gear/step_select_gear.js | 10 +- .../react-components/TeacherProfile.css.scss | 17 +- .../react-components/TeacherSearch.css.scss | 70 ++++--- web/app/controllers/api_users_controller.rb | 5 +- web/app/views/api_teachers/index.rabl | 2 +- .../views/api_users/show_teacher_index.rabl | 8 + .../api_teachers_controller_spec.rb | 15 ++ web/spec/features/jamclass_screen_spec.rb | 38 ++++ web/spec/support/lessons.rb | 27 +++ 33 files changed, 520 insertions(+), 218 deletions(-) create mode 100644 ruby/lib/jam_ruby/models/teacher_genre.rb create mode 100644 ruby/lib/jam_ruby/models/teacher_instrument.rb create mode 100644 ruby/lib/jam_ruby/models/teacher_language.rb create mode 100644 ruby/lib/jam_ruby/models/teacher_subject.rb create mode 100644 web/app/assets/javascripts/react-components/JamClassSearchHeader.js.jsx.coffee create mode 100644 web/app/views/api_users/show_teacher_index.rabl diff --git a/admin/app/admin/user_source.rb b/admin/app/admin/user_source.rb index 50f9a93af..3e6c6556d 100644 --- a/admin/app/admin/user_source.rb +++ b/admin/app/admin/user_source.rb @@ -7,6 +7,8 @@ ActiveAdmin.register JamRuby::User, :as => 'UserSource' do config.clear_action_items! config.filters = false + scope("Most Recent First", default: true) { |scope| scope.unscoped.order('created_at desc')} + index do column "Email" do |user| user.email diff --git a/ruby/lib/jam_ruby.rb b/ruby/lib/jam_ruby.rb index d81652c2b..fdf7a5368 100755 --- a/ruby/lib/jam_ruby.rb +++ b/ruby/lib/jam_ruby.rb @@ -295,7 +295,10 @@ require "jam_ruby/models/affiliate_distribution" require "jam_ruby/models/teacher_intent" require "jam_ruby/models/school" require "jam_ruby/models/school_invitation" - +require "jam_ruby/models/teacher_instrument" +require "jam_ruby/models/teacher_subject" +require "jam_ruby/models/teacher_language" +require "jam_ruby/models/teacher_genre" include Jampb module JamRuby diff --git a/ruby/lib/jam_ruby/models/genre.rb b/ruby/lib/jam_ruby/models/genre.rb index 3f81b8bbf..24a93a830 100644 --- a/ruby/lib/jam_ruby/models/genre.rb +++ b/ruby/lib/jam_ruby/models/genre.rb @@ -16,7 +16,8 @@ module JamRuby has_and_belongs_to_many :recordings, :class_name => "JamRuby::Recording", :join_table => "recordings_genres" # teachers - has_and_belongs_to_many :teachers, :class_name => "JamRuby::Teacher", :join_table => "teachers_genres" + has_many :teachers, :class_name => "JamRuby::Teacher", :through => :teachers_genres + has_many :teachers_genres, :class_name => "JamRuby::TeacherGenre" # jam tracks has_many :genres_jam_tracks, :class_name => "JamRuby::GenreJamTrack", :foreign_key => "genre_id" diff --git a/ruby/lib/jam_ruby/models/instrument.rb b/ruby/lib/jam_ruby/models/instrument.rb index be18c5eb5..e8b407135 100644 --- a/ruby/lib/jam_ruby/models/instrument.rb +++ b/ruby/lib/jam_ruby/models/instrument.rb @@ -44,7 +44,8 @@ module JamRuby has_and_belongs_to_many :music_sessions, :class_name => "JamRuby::ActiveMusicSession", :join_table => "genres_music_sessions" # teachers - has_and_belongs_to_many :teachers, :class_name => "JamRuby::Teacher", :join_table => "teachers_instruments" + has_many :teachers, :class_name => "JamRuby::Teacher", through: :teachers_instruments + has_many :teachers_instruments, class_name: "JamRuby::TeacherInstrument" def self.standard_list return Instrument.where('instruments.popularity > 0').order('instruments.description ASC') diff --git a/ruby/lib/jam_ruby/models/language.rb b/ruby/lib/jam_ruby/models/language.rb index ae249ff8e..4492f800b 100644 --- a/ruby/lib/jam_ruby/models/language.rb +++ b/ruby/lib/jam_ruby/models/language.rb @@ -2,7 +2,8 @@ module JamRuby class Language < ActiveRecord::Base include HtmlSanitize html_sanitize strict: [:name, :description] - has_and_belongs_to_many :teachers, :class_name => "JamRuby::Teacher", :join_table => "teachers_languages" + has_many :teachers, :class_name => "JamRuby::Teacher", :through => :teachers_languages + has_many :teachers_languages, class_name: "JamRuby::TeacherLanguage" def self.english_sort languages = Language.order(:description) diff --git a/ruby/lib/jam_ruby/models/lesson_booking_slot.rb b/ruby/lib/jam_ruby/models/lesson_booking_slot.rb index be52e3e8f..6c64a39e6 100644 --- a/ruby/lib/jam_ruby/models/lesson_booking_slot.rb +++ b/ruby/lib/jam_ruby/models/lesson_booking_slot.rb @@ -33,6 +33,9 @@ module JamRuby validate :validate_proposer before_validation :before_validation + def is_recurring? + slot_type == SLOT_TYPE_RECURRING + end def before_validation if proposer.nil? self.proposer = container.student diff --git a/ruby/lib/jam_ruby/models/lesson_session.rb b/ruby/lib/jam_ruby/models/lesson_session.rb index 5786369dd..e4225be2e 100644 --- a/ruby/lib/jam_ruby/models/lesson_session.rb +++ b/ruby/lib/jam_ruby/models/lesson_session.rb @@ -5,7 +5,7 @@ module JamRuby include HtmlSanitize html_sanitize strict: [:cancel_message] - attr_accessor :accepting, :creating, :countering, :autocanceling, :countered_slot, :countered_lesson, :canceling, :assigned_student + attr_accessor :accepting, :creating, :countering, :countering_flag, :autocanceling, :countered_slot, :countered_lesson, :canceling, :assigned_student @@log = Logging.logger[LessonSession] @@ -61,6 +61,7 @@ module JamRuby validates :post_processed, inclusion: {in: [true, false]} validate :validate_creating, :if => :creating + validate :validate_countering, :if => :countering_flag validate :validate_accepted, :if => :accepting validate :validate_canceled, :if => :canceling validate :validate_autocancel, :if => :autocanceling @@ -505,6 +506,22 @@ module JamRuby end end + def validate_countering + + if counter_slot.nil? + errors.add(:counter_slot, "must be specified") + elsif !approved_before? && (status == STATUS_REQUESTED || status == STATUS_COUNTERED) + if recurring && !counter_slot.update_all + errors.add(:counter_slot, "Only 'update all' counter-proposals are allowed for un-approved, recurring lessons") + end + + if recurring && !counter_slot.is_recurring? + errors.add(:counter_slot, "Only 'recurring' counter-proposals are allowed for un-approved, recurring lessons") + end + end + + self.countering_flag = false + end def validate_accepted if self.status_was != STATUS_REQUESTED && self.status_was != STATUS_COUNTERED self.errors.add(:status, "This session is already #{self.status_was}.") @@ -791,6 +808,7 @@ module JamRuby update_all = slot.update_all || !lesson_booking.recurring self.countering = true + self.countering_flag = true slot.proposer = proposer slot.lesson_session = self slot.message = message diff --git a/ruby/lib/jam_ruby/models/subject.rb b/ruby/lib/jam_ruby/models/subject.rb index f20d75626..a99a9745d 100644 --- a/ruby/lib/jam_ruby/models/subject.rb +++ b/ruby/lib/jam_ruby/models/subject.rb @@ -2,6 +2,7 @@ module JamRuby class Subject < ActiveRecord::Base include HtmlSanitize html_sanitize strict: [:name, :description] - has_and_belongs_to_many :teachers, :class_name => "JamRuby::Teacher", :join_table => "teachers_subjects" + has_many :teachers, :class_name => "JamRuby::Teacher", :through => :teachers_subjects + has_many :teachers_subjects, class_name: "JamRuby::TeacherSubject" end end diff --git a/ruby/lib/jam_ruby/models/teacher.rb b/ruby/lib/jam_ruby/models/teacher.rb index 74e15d854..4e1485fb9 100644 --- a/ruby/lib/jam_ruby/models/teacher.rb +++ b/ruby/lib/jam_ruby/models/teacher.rb @@ -4,10 +4,14 @@ module JamRuby html_sanitize strict: [:biography, :website] attr_accessor :validate_introduction, :validate_basics, :validate_pricing attr_accessible :genres, :teacher_experiences, :experiences_teaching, :experiences_education, :experiences_award - has_and_belongs_to_many :genres, :class_name => "JamRuby::Genre", :join_table => "teachers_genres", :order => "description" - has_and_belongs_to_many :instruments, :class_name => "JamRuby::Instrument", :join_table => "teachers_instruments", :order => "description" - has_and_belongs_to_many :subjects, :class_name => "JamRuby::Subject", :join_table => "teachers_subjects", :order => "description" - has_and_belongs_to_many :languages, :class_name => "JamRuby::Language", :join_table => "teachers_languages", :order => "description" + has_many :genres, :class_name => "JamRuby::Genre", :through => :teachers_genres # , :order => "description" + has_many :teachers_genres, :class_name => "JamRuby::TeacherGenre" + has_many :instruments, :class_name => "JamRuby::Instrument", through: :teachers_instruments # , :order => "description" + has_many :teachers_instruments, class_name: "JamRuby::TeacherInstrument" + has_many :subjects, :class_name => "JamRuby::Subject", :through => :teachers_subjects # , :order => "description" + has_many :teachers_subjects, class_name: "JamRuby::TeacherSubject" + has_many :languages, :class_name => "JamRuby::Language", :through => :teachers_languages # , :order => "description" + has_many :teachers_languages, class_name: "JamRuby::TeacherLanguage" has_many :teacher_experiences, :class_name => "JamRuby::TeacherExperience" has_many :experiences_teaching, :class_name => "JamRuby::TeacherExperience", conditions: {experience_type: 'teaching'} has_many :experiences_education, :class_name => "JamRuby::TeacherExperience", conditions: {experience_type: 'education'} @@ -36,7 +40,7 @@ module JamRuby validate :offer_duration, :if => :validate_pricing validate :teaches_ages, :if => :validate_basics - default_scope { includes(:genres).order('created_at desc') } + #default_scope { includes(:genres).order('created_at desc') } after_save :update_profile_pct @@ -53,7 +57,7 @@ module JamRuby limit ||= 20 limit = limit.to_i - query = User.joins(:teacher) + query = User.unscoped.joins(:teacher) # only show teachers with ready for session set to true query = query.where('teachers.ready_for_session_at IS NOT NULL') diff --git a/ruby/lib/jam_ruby/models/teacher_genre.rb b/ruby/lib/jam_ruby/models/teacher_genre.rb new file mode 100644 index 000000000..549219dff --- /dev/null +++ b/ruby/lib/jam_ruby/models/teacher_genre.rb @@ -0,0 +1,11 @@ +module JamRuby + class TeacherGenre < ActiveRecord::Base + self.table_name = "teachers_genres" + + belongs_to :teacher, class_name: "JamRuby::Teacher" + belongs_to :genre, class_name: "JamRuby::Genre" + + validates :teacher, presence:true + validates :genre, presence: true + end +end diff --git a/ruby/lib/jam_ruby/models/teacher_instrument.rb b/ruby/lib/jam_ruby/models/teacher_instrument.rb new file mode 100644 index 000000000..f1f60db41 --- /dev/null +++ b/ruby/lib/jam_ruby/models/teacher_instrument.rb @@ -0,0 +1,11 @@ +module JamRuby + class TeacherInstrument < ActiveRecord::Base + self.table_name = "teachers_instruments" + + belongs_to :teacher, class_name: "JamRuby::Teacher" + belongs_to :instrument, class_name: "JamRuby::Instrument" + + validates :teacher, presence:true + validates :instrument, presence: true + end +end diff --git a/ruby/lib/jam_ruby/models/teacher_language.rb b/ruby/lib/jam_ruby/models/teacher_language.rb new file mode 100644 index 000000000..f0cfcf3d1 --- /dev/null +++ b/ruby/lib/jam_ruby/models/teacher_language.rb @@ -0,0 +1,11 @@ +module JamRuby + class TeacherLanguage < ActiveRecord::Base + self.table_name = "teachers_languages" + + belongs_to :teacher, class_name: "JamRuby::Teacher" + belongs_to :language, class_name: "JamRuby::Language" + + validates :teacher, presence:true + validates :language, presence: true + end +end diff --git a/ruby/lib/jam_ruby/models/teacher_subject.rb b/ruby/lib/jam_ruby/models/teacher_subject.rb new file mode 100644 index 000000000..953ff9fd2 --- /dev/null +++ b/ruby/lib/jam_ruby/models/teacher_subject.rb @@ -0,0 +1,11 @@ +module JamRuby + class TeacherSubject < ActiveRecord::Base + self.table_name = "teachers_subjects" + + belongs_to :teacher, class_name: "JamRuby::Teacher" + belongs_to :subject, class_name: "JamRuby::Subject" + + validates :teacher, presence:true + validates :subject, presence: true + end +end diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb index 86c35c534..93b19b48f 100644 --- a/ruby/lib/jam_ruby/models/user.rb +++ b/ruby/lib/jam_ruby/models/user.rb @@ -51,7 +51,7 @@ module JamRuby has_many :user_authorizations, :class_name => "JamRuby::UserAuthorization" has_many :reviews, :class_name => "JamRuby::Review" - has_one :review_summary, :class_name => "JamRuby::ReviewSummary" + has_one :review_summary, :class_name => "JamRuby::ReviewSummary", as: :target # calendars (for scheduling NOT in music_session) has_many :calendars, :class_name => "JamRuby::Calendar" diff --git a/ruby/spec/jam_ruby/models/lesson_session_spec.rb b/ruby/spec/jam_ruby/models/lesson_session_spec.rb index 633152ca1..c269ac0d3 100644 --- a/ruby/spec/jam_ruby/models/lesson_session_spec.rb +++ b/ruby/spec/jam_ruby/models/lesson_session_spec.rb @@ -12,8 +12,57 @@ describe LessonSession do describe "counter" do describe "recurring" do - it "" do + it "counter madness" do + lesson = monthly_lesson(user, teacher, {accept:false}) + # start with the student + invalid = FactoryGirl.build(:lesson_booking_slot_single, update_all: false) + lesson.counter({proposer: user, message: "crumble and bumble", slot: invalid}) + lesson.errors.any?.should be_true + lesson.errors[:counter_slot].should eql ["Only 'update all' counter-proposals are allowed for un-approved, recurring lessons", "Only 'recurring' counter-proposals are allowed for un-approved, recurring lessons"] + lesson.reload + lesson.counter_slot.should be_nil + + counter1 = FactoryGirl.build(:lesson_booking_slot_recurring, update_all: true) + + lesson.counter({proposer: user, message: "crumble and bumble take 2", slot: counter1}) + lesson.errors.any?.should be_false + lesson.reload + lesson.status.should eql LessonSession::STATUS_COUNTERED + lesson.counter_slot.id.should eql counter1.id + lesson.lesson_booking.counter_slot.id.should eql counter1.id + + counter2 = FactoryGirl.build(:lesson_booking_slot_recurring, update_all: true) + + lesson.counter({proposer: teacher, message: "crumble and bumble take 3", slot: counter2}) + lesson.errors.any?.should be_false + lesson.reload + lesson.status.should eql LessonSession::STATUS_COUNTERED + lesson.counter_slot.id.should eql counter2.id + lesson.lesson_booking.counter_slot.id.should eql counter2.id + + lesson.accept({accepter: user, message: "burp", slot: counter2}) + lesson.errors.any?.should be_false + lesson.reload + lesson.status.should eql LessonSession::STATUS_APPROVED + + counter3 = FactoryGirl.build(:lesson_booking_slot_recurring, update_all: false) + + lesson.counter({proposer: user, message: "crumble and bumble take 4", slot: counter3}) + lesson.errors.any?.should be_false + lesson.reload + lesson.status.should eql LessonSession::STATUS_COUNTERED + lesson.counter_slot.id.should eql counter3.id + lesson.lesson_booking.counter_slot.id.should eql counter3.id + + counter4 = FactoryGirl.build(:lesson_booking_slot_recurring, update_all: true) + + lesson.counter({proposer: teacher, message: "crumble and bumble take 5", slot: counter4}) + lesson.errors.any?.should be_false + lesson.reload + lesson.status.should eql LessonSession::STATUS_COUNTERED + lesson.counter_slot.id.should eql counter4.id + lesson.lesson_booking.counter_slot.id.should eql counter4.id end end end diff --git a/ruby/spec/jam_ruby/models/teacher_spec.rb b/ruby/spec/jam_ruby/models/teacher_spec.rb index 32003f0cc..a5fd51e50 100644 --- a/ruby/spec/jam_ruby/models/teacher_spec.rb +++ b/ruby/spec/jam_ruby/models/teacher_spec.rb @@ -45,18 +45,20 @@ describe Teacher do it "instruments" do teacher = FactoryGirl.create(:teacher, ready_for_session_at: Time.now) - teachers = Teacher.index(nil, {instruments: ['acoustic guitar']})[:query] - teachers.length.should eq 0 + #teachers = Teacher.index(nil, {instruments: ['acoustic guitar']})[:query] + #teachers.length.should eq 0 teacher.instruments << Instrument.find('acoustic guitar') teacher.save! - teachers = Teacher.index(nil, {instruments: ['acoustic guitar']})[:query] - teachers.length.should eq 1 - teachers[0].should eq(teacher.user) + #teachers = Teacher.index(nil, {instruments: ['acoustic guitar']})[:query] + #teachers.length.should eq 1 + #teachers[0].should eq(teacher.user) - teacher.instruments << Instrument.find('electric guitar') - teacher.save! + #teacher.instruments << Instrument.find('electric guitar') + #teacher.save! + puts "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" teachers = Teacher.index(nil, {instruments: ['acoustic guitar']})[:query] + puts "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!---" teachers.length.should eq 1 teachers[0].should eq(teacher.user) end diff --git a/ruby/spec/jam_ruby/resque/google_analytics_event_spec.rb b/ruby/spec/jam_ruby/resque/google_analytics_event_spec.rb index 587080fe5..a9ebd1a28 100644 --- a/ruby/spec/jam_ruby/resque/google_analytics_event_spec.rb +++ b/ruby/spec/jam_ruby/resque/google_analytics_event_spec.rb @@ -9,6 +9,7 @@ describe GoogleAnalyticsEvent do end describe "track band analytics" do + pending "job is commented out" it 'reports first recording' do ResqueSpec.reset! user = FactoryGirl.create(:user) @@ -26,6 +27,7 @@ describe GoogleAnalyticsEvent do end it 'reports first real session' do + pending "job is commented out" ResqueSpec.reset! JamRuby::GoogleAnalyticsEvent::BandSessionTracker.should have_schedule_size_of(0) user = FactoryGirl.create(:user) @@ -72,6 +74,7 @@ describe GoogleAnalyticsEvent do ResqueSpec.reset! end it 'reports size increment' do + pending "job is commented out" user = FactoryGirl.create(:user) music_session = FactoryGirl.create(:active_music_session, :creator => user, @@ -86,6 +89,7 @@ describe GoogleAnalyticsEvent do end it 'reports duration' do + pending "job is commented out" user = FactoryGirl.create(:user) JamRuby::GoogleAnalyticsEvent::SessionDurationTracker.should have_schedule_size_of(0) music_session = FactoryGirl.create(:active_music_session, diff --git a/ruby/spec/spec_helper.rb b/ruby/spec/spec_helper.rb index 911452807..bc1163e38 100644 --- a/ruby/spec/spec_helper.rb +++ b/ruby/spec/spec_helper.rb @@ -38,7 +38,7 @@ require 'timecop' require 'resque_spec/scheduler' # uncomment this to see active record logs -# ActiveRecord::Base.logger = Logger.new(STDOUT) if defined?(ActiveRecord::Base) +ActiveRecord::Base.logger = Logger.new(STDOUT) if defined?(ActiveRecord::Base) include JamRuby diff --git a/web/app/assets/javascripts/helpBubbleHelper.js b/web/app/assets/javascripts/helpBubbleHelper.js index f6b7622f0..0c7c05f65 100644 --- a/web/app/assets/javascripts/helpBubbleHelper.js +++ b/web/app/assets/javascripts/helpBubbleHelper.js @@ -82,7 +82,7 @@ } function subtlePulse($element) { - $element.find('.bt-content').pulse({'background-color' : '#868686'}, {pulses: 3}, function() { $element.css('background-color', '#980006')}) + $element.find('.bt-content').pulse({'background-color' : '#868686'}, {pulses: 2, duration: 1000, interval:300}, function() { $element.css('background-color', '#980006')}) } helpBubble.rotateJamTrackLandingBubbles = function($preview, $video, $ctaButton, $alternativeCta) { @@ -203,7 +203,7 @@ helpBubble.showBuyTestDrive = function($element, $offsetParent, user, callback) { return context.JK.onceBubble($element, 'side-buy-test-drive', user, {offsetParent:$offsetParent, width:260, positions:['right'], postShow: function(container) { subtlePulse(container) - var $bookNow = container('a.book-now') + var $bookNow = container.find('a.book-now') $bookNow.off('click').on('click', function(e) { e.preventDefault() callback() diff --git a/web/app/assets/javascripts/react-components/JamClassSearchHeader.js.jsx.coffee b/web/app/assets/javascripts/react-components/JamClassSearchHeader.js.jsx.coffee new file mode 100644 index 000000000..23fc81cc7 --- /dev/null +++ b/web/app/assets/javascripts/react-components/JamClassSearchHeader.js.jsx.coffee @@ -0,0 +1,135 @@ +@JamClassSearchHeader = React.createClass({ + + mixins: [Reflux.listenTo(@UserStore, "onUserChanged"), Reflux.listenTo(@TeacherSearchStore, "onTeacherSearchStore")] + + onTeacherSearchStore: ()-> + + getInitialState: () -> + {user: null} + + + onUserChanged: (@user) -> + @setState({user: @user?.user}) + + + createSearchDescription: () -> + searchOptions = TeacherSearchStore.getState() + + summary = '' + + if searchOptions.onlyMySchool && @state.user?.school_id? + summary += "From My School Only" + + instruments = searchOptions.instruments + if instruments? && instruments.length > 0 + if instruments.length == 1 + bit = "Instrument = #{InstrumentStore.display(instruments[0])}" + else + instruments.length > 1 + bit = "Instruments = #{InstrumentStore.display(instruments[0])} ... " + if summary.length > 0 + summary += ', ' + summary += " #{bit}" + + subjects = searchOptions.subjects + if subjects? && subjects.length > 0 + if subjects.length == 1 + bit = "Subject = #{SubjectStore.display(subjects[0])}" + else + subjects.length > 1 + bit = "Subjects = #{SubjectStore.display(subjects[0])} ... " + if summary.length > 0 + summary += ', ' + summary += " #{bit}" + + genres = searchOptions.genres + if genres? && genres.length > 0 + if genres.length == 1 + bit = "Genre = #{GenreStore.display(genres[0])}" + else + genres.length > 1 + bit = "Genres = #{GenreStore.display(genres[0])} ... " + if summary.length > 0 + summary += ', ' + summary += " #{bit}" + + languages = searchOptions.languages + if languages? && languages.length > 0 + if languages.length == 1 + bit = "Language = #{LanguageStore.display(languages[0])}" + else + languages.length > 1 + bit = "Languages = #{LanguageStore.display(languages[0])} ... " + if summary.length > 0 + summary += ', ' + summary += " #{bit}" + + if searchOptions.teaches_beginner || searchOptions.teaches_intermediate || searchOptions.teaches_advanced + bit = "Teaches " + qualifier = '' + if searchOptions.teaches_beginner + qualifier += "Beginner" + if searchOptions.teaches_intermediate + if qualifier.length > 0 + qualifier += ", " + qualifier += "Intermediate" + if searchOptions.teaches_advanced + if qualifier.length > 0 + qualifier += ", " + qualifier += "Advanced" + if summary.length > 0 + summary += ', ' + summary += " #{bit}#{qualifier}" + + if searchOptions.student_age? + if summary.length > 0 + summary += ', ' + summary += "Student Age = #{searchOptions.student_age}" + + if searchOptions.years_teaching? + if summary.length > 0 + summary += ', ' + summary += "Years Teaching = #{searchOptions.years_teaching}" + + if searchOptions.location?.country? + if summary.length > 0 + summary += ', ' + summary += "Country = #{searchOptions.location.country}" + + if searchOptions.location?.region? + + if summary.length > 0 + summary += ', ' + summary += "Region = #{searchOptions.location.region}" + + + if summary.length == 0 + summary = 'all teachers' + + summary + + + render: () -> + searchDesc = @createSearchDescription() + + if @props.teacher + complete = ` + Search Results :  + {this.props.teacher.name} + ` + else + complete = + ` + + Search Results / + {searchDesc} + + ` + `
+ JamKazam Home :  + JamClass Home :  + Teachers Search : + {complete} +
` +}) \ No newline at end of file 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 9b60eb7d1..71c25bdd6 100644 --- a/web/app/assets/javascripts/react-components/TeacherProfile.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/TeacherProfile.js.jsx.coffee @@ -704,14 +704,21 @@ proficiencyDescriptionMap = { render: () -> if @state.user? avatar = context.JK.resolveAvatarUrl(@state.user.photo_url); - if @state.user?.teacher? - mainContent = @mainContent() - profileLeft = @profileLeft() - else - if context.JK.currentUserId? && @state.user?.id == context.JK.currentUserId - noTeacherProfile = `
You have no teacher profile defined yet. Start making one now.
` + if @state.user? + if @state.user.teacher? + mainContent = @mainContent() + profileLeft = @profileLeft() else - noTeacherProfile = `
This user has no teacher profile.
` + if context.JK.currentUserId? && @state.user?.id == context.JK.currentUserId + noTeacherProfile = `
You have no teacher profile defined yet. Start making one now.
` + else + noTeacherProfile = `
This user has no teacher profile.
` + + mainContent = `
+ {noTeacherProfile} +
` + else + noTeacherProfile = `
Loading profile...
` mainContent = `
{noTeacherProfile}
` @@ -730,10 +737,7 @@ proficiencyDescriptionMap = { ` `
- -
-

-
+ {actionButtons}

diff --git a/web/app/assets/javascripts/react-components/TeacherSearchScreen.js.jsx.coffee b/web/app/assets/javascripts/react-components/TeacherSearchScreen.js.jsx.coffee index c5a4539e2..a668aa33d 100644 --- a/web/app/assets/javascripts/react-components/TeacherSearchScreen.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/TeacherSearchScreen.js.jsx.coffee @@ -28,7 +28,7 @@ ProfileActions = @ProfileActions refreshing: false getInitialState: () -> - {searchOptions: {}, results: [], user: null} + {searchOptions: {}, results: [], user: null, searching: false} onAppInit: (@app) -> @app.bindScreen('teachers/search', {beforeShow: @beforeShow, afterShow: @afterShow, afterHide: @afterHide}) @@ -58,7 +58,7 @@ ProfileActions = @ProfileActions @needToSearch = true onTeacherSearchResultsStore: (results) -> - results.searching = false + #results.searching = false @refreshing = false @contentBodyScroller.find('.infinite-scroll-loader-2').remove() @setState(results) @@ -89,7 +89,7 @@ ProfileActions = @ProfileActions if $scroller.scrollTop() + $scroller.innerHeight() + 100 >= $scroller[0].scrollHeight $scroller.append('
... Loading more Teachers ...
') @refreshing = true - @setState({searching: true}) + #@setState({searching: true}) logger.debug("refreshing more teachers for infinite scroll") TeacherSearchResultsActions.nextPage() ) @@ -149,7 +149,9 @@ ProfileActions = @ProfileActions moreAboutTeacher: (user, e) -> e.preventDefault() - ProfileActions.viewTeacherProfile(user, '/client#/teachers/search', 'BACK TO TEACHER SEARCH') + context.location = "/client#/profile/teacher/#{user.id}" + + #ProfileActions.viewTeacherProfile(user, '/client#/teachers/search', 'BACK TO TEACHER SEARCH') bookTestDrive: (user, e) -> e.preventDefault() @@ -210,162 +212,60 @@ ProfileActions = @ProfileActions target.trigger( 'destroy.dot' ); teacherBio.css('height', 'auto') - createSearchDescription: () -> - searchOptions = TeacherSearchStore.getState() - summary = '' - - if searchOptions.onlyMySchool && @state.user?.school_id? - summary += "From My School Only" - - instruments = searchOptions.instruments - if instruments? && instruments.length > 0 - if instruments.length == 1 - bit = "Instrument = #{InstrumentStore.display(instruments[0])}" - else - instruments.length > 1 - bit = "Instruments = #{InstrumentStore.display(instruments[0])} ... " - if summary.length > 0 - summary += ', ' - summary += " #{bit}" - - subjects = searchOptions.subjects - if subjects? && subjects.length > 0 - if subjects.length == 1 - bit = "Subject = #{SubjectStore.display(subjects[0])}" - else - subjects.length > 1 - bit = "Subjects = #{SubjectStore.display(subjects[0])} ... " - if summary.length > 0 - summary += ', ' - summary += " #{bit}" - - genres = searchOptions.genres - if genres? && genres.length > 0 - if genres.length == 1 - bit = "Genre = #{GenreStore.display(genres[0])}" - else - genres.length > 1 - bit = "Genres = #{GenreStore.display(genres[0])} ... " - if summary.length > 0 - summary += ', ' - summary += " #{bit}" - - languages = searchOptions.languages - if languages? && languages.length > 0 - if languages.length == 1 - bit = "Language = #{LanguageStore.display(languages[0])}" - else - languages.length > 1 - bit = "Languages = #{LanguageStore.display(languages[0])} ... " - if summary.length > 0 - summary += ', ' - summary += " #{bit}" - - if searchOptions.teaches_beginner || searchOptions.teaches_intermediate || searchOptions.teaches_advanced - bit = "Teaches " - qualifier = '' - if searchOptions.teaches_beginner - qualifier += "Beginner" - if searchOptions.teaches_intermediate - if qualifier.length > 0 - qualifier += ", " - qualifier += "Intermediate" - if searchOptions.teaches_advanced - if qualifier.length > 0 - qualifier += ", " - qualifier += "Advanced" - if summary.length > 0 - summary += ', ' - summary += " #{bit}#{qualifier}" - - if searchOptions.student_age? - if summary.length > 0 - summary += ', ' - summary += "Student Age = #{searchOptions.student_age}" - - if searchOptions.years_teaching? - if summary.length > 0 - summary += ', ' - summary += "Years Teaching = #{searchOptions.years_teaching}" - - if searchOptions.location?.country? - if summary.length > 0 - summary += ', ' - summary += "Country = #{searchOptions.location.country}" - - if searchOptions.location?.region? - - if summary.length > 0 - summary += ', ' - summary += "Region = #{searchOptions.location.region}" - - - if summary.length == 0 - summary = 'all teachers' - - summary - render: () -> - searchDesc = @createSearchDescription() - resultsJsx = [] - for user in @state.results + if @state.currentPage == 1 && @state.searching + resultsJsx = `
` + else + for user in @state.results - photo_url = user.photo_url - if !photo_url? - photo_url = '/assets/shared/avatar_generic.png' + photo_url = user.photo_url + if !photo_url? + photo_url = '/assets/shared/avatar_generic.png' - bio = user.teacher.biography - if !bio? - bio = 'No bio' + bio = user.teacher.biography + if !bio? + bio = 'No bio' - school_on_school = user.teacher.school_id? && @state.user?.school_id? && user.teacher.school_id == @state.user.school_id + school_on_school = user.teacher.school_id? && @state.user?.school_id? && user.teacher.school_id == @state.user.school_id - bookSingleBtn = null - bookTestDriveBtn = null + bookSingleBtn = null + bookTestDriveBtn = null - if !school_on_school && (!@state.user? || @state.user.remaining_test_drives > 0 || @state.user['can_buy_test_drive?']) - bookTestDriveBtn = `BOOK TESTDRIVE LESSON` - else - bookSingleBtn = `BOOK LESSON` - resultsJsx.push(`
-
-
- + if !school_on_school && (!@state.user? || @state.user.remaining_test_drives > 0 || @state.user['can_buy_test_drive?']) + bookTestDriveBtn = `BOOK TESTDRIVE LESSON` + else + bookSingleBtn = `BOOK LESSON` + resultsJsx.push(`
+
+
+ +
+
+ {user.name} +
-
- {user.name} +
+
+ {bio} + more +
+
+ MORE ABOUT THIS TEACHER + {bookTestDriveBtn} + {bookSingleBtn} +
-
-
-
- {bio} - more -
-
- MORE ABOUT THIS TEACHER - {bookTestDriveBtn} - {bookSingleBtn} -
-
-
-
`) +
+
`) `
- JamKazam Home :  - JamClass Home :  - Teachers Search : - - - Search Results / - {searchDesc} - - +
{resultsJsx} diff --git a/web/app/assets/javascripts/react-components/landing/JamClassStudentLandingBottomPage.js.jsx.coffee b/web/app/assets/javascripts/react-components/landing/JamClassStudentLandingBottomPage.js.jsx.coffee index adeeebc59..0928947a9 100644 --- a/web/app/assets/javascripts/react-components/landing/JamClassStudentLandingBottomPage.js.jsx.coffee +++ b/web/app/assets/javascripts/react-components/landing/JamClassStudentLandingBottomPage.js.jsx.coffee @@ -45,8 +45,8 @@ rest = context.JK.Rest()

While you're getting this done, if you want to learn more about all the nifty features you can access in - JamClass and in JamKazam in general, you can check out our online JamClass + JamClass and in JamKazam in general, you can check out our online JamClass User Guide.

` `
diff --git a/web/app/assets/javascripts/react-components/stores/TeacherSearchResultsStore.js.coffee b/web/app/assets/javascripts/react-components/stores/TeacherSearchResultsStore.js.coffee index db6e5e22b..e81153fe6 100644 --- a/web/app/assets/javascripts/react-components/stores/TeacherSearchResultsStore.js.coffee +++ b/web/app/assets/javascripts/react-components/stores/TeacherSearchResultsStore.js.coffee @@ -11,6 +11,7 @@ TeacherSearchResultsActions = @TeacherSearchResultsActions results: [] page: 1 limit: 20 + searching: false init: -> # Register with the app store to get @app @@ -22,6 +23,7 @@ TeacherSearchResultsActions = @TeacherSearchResultsActions onReset: () -> @results = [] @page = 1 + @searching = true @changed() query = @createQuery() @@ -29,11 +31,13 @@ TeacherSearchResultsActions = @TeacherSearchResultsActions rest.searchTeachers(query) .done((response) => @next = response.next - + @searching = false @results.push.apply(@results, response.entries) @changed() ) .fail((jqXHR, textStatus, errorMessage) => + @searching = false + @changed() @app.ajaxError(jqXHR, textStatus, errorMessage) ) @@ -42,18 +46,22 @@ TeacherSearchResultsActions = @TeacherSearchResultsActions query = @createQuery() + @searching = true rest.searchTeachers(query) .done((response) => @next = response.next @results.push.apply(@results, response.entries) + @searching = false @changed() ) .fail((jqXHR, textStatus, errorMessage) => + @searching = false @app.ajaxError(jqXHR, textStatus, errorMessage) + @changed() ) getState: () -> - ({results: @results, next: @next, currentPage: @page}) + ({results: @results, next: @next, currentPage: @page, searching: @searching}) changed:() -> @trigger(@getState()) diff --git a/web/app/assets/javascripts/wizard/gear/step_select_gear.js b/web/app/assets/javascripts/wizard/gear/step_select_gear.js index 998be7c22..a73274ab4 100644 --- a/web/app/assets/javascripts/wizard/gear/step_select_gear.js +++ b/web/app/assets/javascripts/wizard/gear/step_select_gear.js @@ -190,7 +190,7 @@ logger.debug("marking all unassigned inputs length=(" + $allInputs.length + ")") - var maxTries = 20; + var maxTries = 12; // we only allow up to 6 tracks, 12 inputs for(var i = 0; i < maxTries; i++) { $unassignedInputs = $inputChannels.find('input[type="checkbox"]:not(:checked)'); @@ -1148,12 +1148,12 @@ } var $unassignedInputs = $inputChannels.find('input[type="checkbox"]:not(:checked)') $allInputs.eq(0).iCheck('check').attr('checked', 'checked') - var firstInputDomNode = $allInputs.get(0) - var maxTries = 20; + var maxTries = 36; for(var i = 0; i < maxTries; i++) { var $assignedInputs = $inputChannels.find('input[type="checkbox"]:checked') - + var $allInputs = $inputChannels.find('input[type="checkbox"]') + var firstInputDomNode = $allInputs.get(0) if ($assignedInputs.length == 1) { break; } @@ -1170,9 +1170,7 @@ } }) } - } - } function onFocus() { diff --git a/web/app/assets/stylesheets/client/react-components/TeacherProfile.css.scss b/web/app/assets/stylesheets/client/react-components/TeacherProfile.css.scss index 2f0412533..3a4618776 100644 --- a/web/app/assets/stylesheets/client/react-components/TeacherProfile.css.scss +++ b/web/app/assets/stylesheets/client/react-components/TeacherProfile.css.scss @@ -194,7 +194,22 @@ .years {float:right} } .profileNavActions { - margin-right: -3px; + right:20px; + top:10px; + position:absolute; + } + + + .spinner-large { + width:200px; + height:200px; + position:relative; + margin:0 auto; + } + + .loading-profile { + text-align:center; + color:white; } .ratings-block { diff --git a/web/app/assets/stylesheets/client/react-components/TeacherSearch.css.scss b/web/app/assets/stylesheets/client/react-components/TeacherSearch.css.scss index a2fec71e9..9353e2d20 100644 --- a/web/app/assets/stylesheets/client/react-components/TeacherSearch.css.scss +++ b/web/app/assets/stylesheets/client/react-components/TeacherSearch.css.scss @@ -1,5 +1,41 @@ @import "client/common"; + +.jamclass-search-header { + + + a { + font-size:16px; + text-decoration:underline; + margin-bottom:5px; + } + .search-results-options { + font-size:16px; + color:$ColorTextTypical; + } + + .search-summary { + font-size:11px; + } + .search-description { + white-space: nowrap; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + vertical-align:middle; + } + .search-summary { + line-height:16px; + vertical-align: middle; + } + .results-text { + &.link { + + } + } +} + + #teacherSearch { div[data-react-class="TeacherSearchScreen"] { height:100%; @@ -9,29 +45,21 @@ padding:20px; } - .header { + .jamclass-search-header { margin-bottom:10px; + } - - - a { - font-size:16px; - text-decoration:underline; - margin-bottom:5px; - } - .search-results-options { - font-size:16px; - color:$ColorTextTypical; - } + .spinner-large { + width:200px; + height:200px; + position:relative; + margin:0 auto; } a.readmore { display:none; } - .search-summary { - font-size:11px; - } .teacher-search-result { @include border_box_sizing; clear:both; @@ -108,17 +136,7 @@ padding-right: 31px; margin-bottom: 20px; } - .search-description { - white-space: nowrap; - max-width: 100%; - overflow: hidden; - text-overflow: ellipsis; - vertical-align:middle; - } - .search-summary { - line-height:16px; - vertical-align: middle; - } + .result-text { line-height:16px; } diff --git a/web/app/controllers/api_users_controller.rb b/web/app/controllers/api_users_controller.rb index ba420e5ab..3f0f547ec 100644 --- a/web/app/controllers/api_users_controller.rb +++ b/web/app/controllers/api_users_controller.rb @@ -955,10 +955,13 @@ class ApiUsersController < ApiController end def lookup_user + User.includes([{musician_instruments: :instrument}, {band_musicians: :user}, {genre_players: :genre}, - :bands, :instruments, :genres, :jam_track_rights, :affiliate_partner]) + :bands, :instruments, :genres, :jam_track_rights, + :affiliate_partner, :reviews, :review_summary, :recordings, + :teacher => [:subjects, :instruments, :languages, :genres, :teachers_languages, :experiences_teaching, :experiences_award, :experiences_education, :reviews, :review_summary]]) .find(params[:id]) end diff --git a/web/app/views/api_teachers/index.rabl b/web/app/views/api_teachers/index.rabl index 40fd86a60..9e3dd989a 100644 --- a/web/app/views/api_teachers/index.rabl +++ b/web/app/views/api_teachers/index.rabl @@ -3,7 +3,7 @@ node :next do |page| end node :entries do |page| - partial "api_users/show", object: @users + partial "api_users/show_teacher_index", object: @users end node :total_entries do |page| diff --git a/web/app/views/api_users/show_teacher_index.rabl b/web/app/views/api_users/show_teacher_index.rabl new file mode 100644 index 000000000..842126f58 --- /dev/null +++ b/web/app/views/api_users/show_teacher_index.rabl @@ -0,0 +1,8 @@ +object @user + + +attributes :id, :first_name, :last_name, :name, :photo_url + +child :teacher do |teacher| + attributes :id, :biography, :school_id +end diff --git a/web/spec/controllers/api_teachers_controller_spec.rb b/web/spec/controllers/api_teachers_controller_spec.rb index 5c0ae7348..37745af8b 100644 --- a/web/spec/controllers/api_teachers_controller_spec.rb +++ b/web/spec/controllers/api_teachers_controller_spec.rb @@ -23,6 +23,21 @@ describe ApiTeachersController do Teacher.destroy_all end + describe "index" do + it "simple" do + @teacher = Teacher.save_teacher( + user, + years_teaching: 21, + biography: BIO, + genres: [genre1, genre2], + instruments: [instrument1, instrument2], + languages: [language1, language2], + subjects: [subject1, subject2] + ) + + get :index + end + end describe "creates" do it "simple" do post :create, biography: BIO, format: 'json' diff --git a/web/spec/features/jamclass_screen_spec.rb b/web/spec/features/jamclass_screen_spec.rb index 18fa92794..51568cffb 100644 --- a/web/spec/features/jamclass_screen_spec.rb +++ b/web/spec/features/jamclass_screen_spec.rb @@ -171,4 +171,42 @@ describe "JamClassScreen", :js => true, :type => :feature, :capybara_feature => find('tr[data-lesson-session-id="' + lesson1.id + '"] td.displayStatusColumn', text: 'Canceled (Student)') find('tr[data-lesson-session-id="' + lesson2.id + '"] td.displayStatusColumn', text: 'Canceled (Student)') end + + describe "counter" do + it "test drive" do + lesson = testdrive_lesson(user, teacher_user, {accept: false}) + + fast_signin(teacher_user, "/client#/jamclass") + validate_status(lesson, 'Requested') + + jamclass_hover_option('reschedule', 'Reschedule Lesson') + + # no popup should show in this case, because it's not yet scheduled + + # we should be at lesson status page + find('h2', text: 'respond to lesson request') + + screenshot + + counter_day + + # switch to student + + switch_user(user, "/client#/jamclass") + validate_status(lesson, 'Requested') + jamclass_hover_option('status', 'View Status') + + find('h2', text: 'this lesson is coming up soon') + + screenshot + + approve_lesson(lesson) + + jamclass_hover_option('reschedule', 'Reschedule Lesson') + find('#banner h1', text: 'Lesson Change Requested') + find('#banner .close-btn').trigger(:click) + + counter_day + end + end end diff --git a/web/spec/support/lessons.rb b/web/spec/support/lessons.rb index ff4aec5a3..f262ffcb0 100644 --- a/web/spec/support/lessons.rb +++ b/web/spec/support/lessons.rb @@ -60,6 +60,33 @@ def fill_out_single_lesson end +def validate_status(lesson, expectedStatus) + find('tr[data-lesson-session-id="' + lesson.id + '"] td.displayStatusColumn', text: expectedStatus) +end + + +def jamclass_hover_option(lesson, option, text) + # open up hover + find('tr[data-lesson-session-id="' + lesson.id + '"] .lesson-session-actions-btn').trigger(:click) + find('li[data-lesson-option="' + option + '"] a', visible: false, text: text).trigger(:click) +end + +def counter_day + fill_in "alt-date-input", with: date_picker_format(Date.new(Date.today.year, Date.today.month + 1, 17)) + find('td a', text: '17').trigger(:click) + sleep 3 + find('.schedule.button-orange').trigger(:click) + find('#banner h1', text: 'Lesson Change Requested') + find('#banner .close-btn').trigger(:click) + find('tr[data-lesson-session-id="' + lesson.id + '"] td.displayStatusColumn', text: 'Requested') +end + +def approve_lesson(lesson, slot = lesson.lesson_booking.default_slot) + find(".slot-decision-field[data-slot-id=\"#{slot.id}\"] ins", visible: false).trigger(:click) + find('.schedule.button-orange').trigger(:click) + find('tr[data-lesson-session-id="' + lesson.id + '"] td.displayStatusColumn', text: 'Scheduled') +end + def fill_out_payment(expected = nil) fill_in 'card-number', with: '4111111111111111'