From 45e882657a7374b2a102bc6ab4be208703a0369a Mon Sep 17 00:00:00 2001 From: "Justin Craig-Kuhn (JCK)" Date: Thu, 19 Oct 2023 11:58:57 -0700 Subject: [PATCH] Add tag filter to song page --- .../concerns/ambiguity/song_title.rb | 16 +++++-- .../{sort_helper.rb => filter_helper.rb} | 48 +++++++++++-------- app/javascript/packs/application.css.scss | 15 +++++- .../shared/_song_track_tag_filter.html.slim | 8 ++++ app/views/shared/_sort_filter.html.slim | 9 ++-- .../_sort_songs_and_venues_filter.html.slim | 5 +- app/views/songs/show.html.slim | 12 ++--- spec/features/songs_spec.rb | 4 +- spec/features/venues_spec.rb | 4 +- spec/support/feature_helpers.rb | 16 +++---- 10 files changed, 88 insertions(+), 49 deletions(-) rename app/helpers/{sort_helper.rb => filter_helper.rb} (77%) create mode 100644 app/views/shared/_song_track_tag_filter.html.slim diff --git a/app/controllers/concerns/ambiguity/song_title.rb b/app/controllers/concerns/ambiguity/song_title.rb index da3c8f54..226dddd8 100644 --- a/app/controllers/concerns/ambiguity/song_title.rb +++ b/app/controllers/concerns/ambiguity/song_title.rb @@ -16,13 +16,21 @@ def hydrate_song_page @next_song = next_song @view = 'songs/show' @ambiguity_controller = 'songs' - @tracks = song.tracks - .includes(:songs, show: :venue, track_tags: :tag) - .order(@order_by) - .paginate(page: params[:page], per_page: params[:per_page].presence || 20) + @tracks = fetch_song_tracks @tracks_likes = user_likes_for_tracks(@tracks) end + def fetch_song_tracks + tracks = song.tracks.includes(:songs, show: :venue, track_tags: :tag).order(@order_by) + tracks = tagged_tracks(tracks) + tracks.paginate(page: params[:page], per_page: params[:per_page].presence || 20) + end + + def tagged_tracks(tracks) + return tracks if params[:tag_slug].blank? || params[:tag_slug] == 'all' + tracks.tagged_with(params[:tag_slug]) + end + def previous_song Song.where('title < ?', @song.title) .order(title: :desc) diff --git a/app/helpers/sort_helper.rb b/app/helpers/filter_helper.rb similarity index 77% rename from app/helpers/sort_helper.rb rename to app/helpers/filter_helper.rb index 82e82f18..983c21e2 100644 --- a/app/helpers/sort_helper.rb +++ b/app/helpers/filter_helper.rb @@ -1,4 +1,22 @@ -module SortHelper +module FilterHelper + def filter_title(param_name, items) + item = items.find { |_k, v| params[param_name] == v }&.first&.html_safe + item.presence || items.first.first.html_safe + end + + def filter(param_name, items) # rubocop:disable Metrics/AbcSize + skippables = %w[controller action name t slug] + [param_name.to_s] + items.map do |k, v| + link = params[param_name] == v ? "#{k}" : k + param_str = "?#{param_name}=#{CGI.escape(v)}" + params.each do |key, val| + next if key.in?(skippables) + param_str += "&#{key}=#{val}" if val.present? + end + tag.li(link_to(link.html_safe, param_str)) + end.join.html_safe + end + def sort_songs_and_venues_links(item_hash) item_hash.map do |k, v| link = params[:sort] == v ? "#{k}" : k @@ -24,25 +42,6 @@ def sort_tags_links(item_hash) str.html_safe end - def sort_filter_link_title(items) - items.each do |k, v| - return k.html_safe if params[:sort] == v || params[:sort].blank? - end - items.first.first.html_safe - end - - def sort_filter(items) - items.map do |k, v| - link = params[:sort] == v ? "#{k}" : k - param_str = "?sort=#{CGI.escape(v)}" - params.each do |key, val| - next if key.in?(%w[controller action name t sort]) - param_str += "&#{key}=#{val}" if val.present? - end - tag.li(link_to(link.html_safe, param_str)) - end.join.html_safe - end - def sort_songs_title(items) items.each_with_index do |(key, val), i| return key.html_safe if (i.zero? && params[:sort].empty?) || @@ -100,4 +99,13 @@ def stored_playlist_sort_items ' Duration' => 'duration' } end + + def song_track_tag_items(song) + tag_data = song.tracks.joins(:tags).distinct.order('tags.name').pluck('tags.name', 'tags.slug') + items = { "  All Tags" => 'all' } + tag_data.each do |name, slug| + items[" #{name}"] = slug + end + items + end end diff --git a/app/javascript/packs/application.css.scss b/app/javascript/packs/application.css.scss index 3cae33b7..5cc53edc 100644 --- a/app/javascript/packs/application.css.scss +++ b/app/javascript/packs/application.css.scss @@ -302,6 +302,7 @@ html { .current { color: $nav_highlight !important; font-weight: bold !important; + margin-right: 3px; } a:hover, a:focus { text-decoration: underline !important; @@ -1086,7 +1087,6 @@ html { width: 100%; border-bottom: 1px solid $hr_gray; margin-top: 10px; - margin-bottom: 5px; } .next_item_link { float: right; @@ -1145,6 +1145,19 @@ html { font-size: 18px; } } + .filter_label { + float: left; + width: 30px; + position: relative; + top: 7px; + font-size: 1.2em; + } + .filter_dropdown { + float: left; + } + .hr { + margin-bottom: 15px; + } .app_callout { * { float: left; diff --git a/app/views/shared/_song_track_tag_filter.html.slim b/app/views/shared/_song_track_tag_filter.html.slim new file mode 100644 index 00000000..ed44c3ca --- /dev/null +++ b/app/views/shared/_song_track_tag_filter.html.slim @@ -0,0 +1,8 @@ +.filter_label Tag +.filter_dropdown + .btn-group + button.btn.btn-default.dropdown-toggle type='button' data-toggle='dropdown' aria-haspopup='true' aria-expanded='false' + => filter_title(:tag_slug, item_hash) + span.caret + ul.dropdown-menu = filter(:tag_slug, item_hash) += clear_both diff --git a/app/views/shared/_sort_filter.html.slim b/app/views/shared/_sort_filter.html.slim index e2b906bd..cb3a05de 100644 --- a/app/views/shared/_sort_filter.html.slim +++ b/app/views/shared/_sort_filter.html.slim @@ -1,7 +1,8 @@ -h3 - |> Sort by +.filter_label Sort +.filter_dropdown .btn-group button.btn.btn-default.dropdown-toggle type='button' data-toggle='dropdown' aria-haspopup='true' aria-expanded='false' - => sort_filter_link_title(item_hash) + => filter_title(:sort, item_hash) span.caret - ul.dropdown-menu = sort_filter(item_hash) + ul.dropdown-menu = filter(:sort, item_hash) += clear_both diff --git a/app/views/shared/_sort_songs_and_venues_filter.html.slim b/app/views/shared/_sort_songs_and_venues_filter.html.slim index 48686d98..1008a218 100644 --- a/app/views/shared/_sort_songs_and_venues_filter.html.slim +++ b/app/views/shared/_sort_songs_and_venues_filter.html.slim @@ -1,7 +1,8 @@ -h3 - |> Sort by +.filter_label Sort +.filter_dropdown .btn-group button.btn.btn-default.dropdown-toggle type='button' data-toggle='dropdown' aria-haspopup='true' aria-expanded='false' => sort_songs_title(item_hash) span.caret ul.dropdown-menu = sort_songs_and_venues_links(item_hash) += clear_both diff --git a/app/views/songs/show.html.slim b/app/views/songs/show.html.slim index 5cc0cfb3..72e3c14b 100644 --- a/app/views/songs/show.html.slim +++ b/app/views/songs/show.html.slim @@ -15,10 +15,14 @@ - if @song.instrumental? h3 (Instrumental) - h3 Total tracks: #{@song.tracks.size} + h3 Total tracks: #{@tracks.size} + + .hr + + = render partial: 'shared/sort_filter', locals: { item_hash: track_sort_items } + = render partial: 'shared/song_track_tag_filter', locals: { item_hash: song_track_tag_items(@song) } .hr - br - if @song.lyrics.present? = link_to "#{tag.i(class: 'glyphicon glyphicon-book')}  Lyrics".html_safe, '#', class: 'song_lyrics btn btn-default', data: { title: @song.title, lyrics: lyrics_for(@song) } @@ -35,10 +39,6 @@ .hr - = render partial: 'shared/sort_filter', locals: { item_hash: track_sort_items } - - .hr - = link_to('<< Previous Song'.html_safe, "/#{@previous_song.slug}") = clear_both = link_to('Next Song >>'.html_safe, "/#{@next_song.slug}", class: 'next_item_link') diff --git a/spec/features/songs_spec.rb b/spec/features/songs_spec.rb index 8b1c3e35..154e6b49 100644 --- a/spec/features/songs_spec.rb +++ b/spec/features/songs_spec.rb @@ -55,7 +55,7 @@ # Default sort by Title within('#title_box') do - expect_content('Sort by', 'Title') + expect_content('Sort', 'Title') end expect_content_in_order([song1, song2, song3].map(&:title)) @@ -63,7 +63,7 @@ within('#title_box') do first('.btn-group').click click_link('Track Count') - expect_content('Sort by', 'Track Count') + expect_content('Sort', 'Track Count') end expect_content_in_order([song2, song1, song3].map(&:title)) end diff --git a/spec/features/venues_spec.rb b/spec/features/venues_spec.rb index 0c65894c..195eab29 100644 --- a/spec/features/venues_spec.rb +++ b/spec/features/venues_spec.rb @@ -58,7 +58,7 @@ # Default sort by Name within('#title_box') do - expect_content('Sort by', 'Name') + expect_content('Sort', 'Name') end expect_content_in_order([venue1, venue2, venue3].map(&:name)) @@ -66,7 +66,7 @@ within('#title_box') do first('.btn-group').click click_link('Show Count') - expect_content('Sort by', 'Show Count') + expect_content('Sort', 'Show Count') end expect_content_in_order([venue3, venue1, venue2].map(&:name)) end diff --git a/spec/support/feature_helpers.rb b/spec/support/feature_helpers.rb index 6bda540a..11097b4f 100644 --- a/spec/support/feature_helpers.rb +++ b/spec/support/feature_helpers.rb @@ -28,7 +28,7 @@ def expect_track_sorting_controls(tracks) # rubocop:disable Metrics/AbcSize # Default sort by Reverse date within('#title_box') do - expect_content('Sort by', 'Reverse Date') + expect_content('Sort', 'Reverse Date') end expect_content_in_order(titles_by_date.reverse) @@ -36,7 +36,7 @@ def expect_track_sorting_controls(tracks) # rubocop:disable Metrics/AbcSize within('#title_box') do first('.dropdown-toggle').click click_link('Date') - expect_content('Sort by', 'Date') + expect_content('Sort', 'Date') end expect_content_in_order(titles_by_date) @@ -44,7 +44,7 @@ def expect_track_sorting_controls(tracks) # rubocop:disable Metrics/AbcSize within('#title_box') do first('.dropdown-toggle').click click_link('Likes') - expect_content('Sort by', 'Likes') + expect_content('Sort', 'Likes') end expect_content_in_order(titles_by_likes) @@ -52,7 +52,7 @@ def expect_track_sorting_controls(tracks) # rubocop:disable Metrics/AbcSize within('#title_box') do first('.dropdown-toggle').click click_link('Duration') - expect_content('Sort by', 'Duration') + expect_content('Sort', 'Duration') end expect_content_in_order(titles_by_duration) end @@ -64,7 +64,7 @@ def expect_show_sorting_controls(shows) # rubocop:disable Metrics/AbcSize # Default sort by Reverse date within('#title_box') do - expect_content('Sort by', 'Reverse Date') + expect_content('Sort', 'Reverse Date') end expect_content_in_order(dates_by_date.reverse) @@ -72,7 +72,7 @@ def expect_show_sorting_controls(shows) # rubocop:disable Metrics/AbcSize within('#title_box') do first('.dropdown-toggle').click click_link('Date') - expect_content('Sort by', 'Date') + expect_content('Sort', 'Date') end expect_content_in_order(dates_by_date) @@ -80,7 +80,7 @@ def expect_show_sorting_controls(shows) # rubocop:disable Metrics/AbcSize within('#title_box') do first('.dropdown-toggle').click click_link('Likes') - expect_content('Sort by', 'Likes') + expect_content('Sort', 'Likes') end expect_content_in_order(dates_by_likes) @@ -88,7 +88,7 @@ def expect_show_sorting_controls(shows) # rubocop:disable Metrics/AbcSize within('#title_box') do first('.dropdown-toggle').click click_link('Duration') - expect_content('Sort by', 'Duration') + expect_content('Sort', 'Duration') end expect_content_in_order(dates_by_duration) end