Class: Www::TowelWarmersController
- Inherits:
-
BasePortalController
- Object
- ActionController::Base
- ApplicationController
- BasePortalController
- Www::TowelWarmersController
- Includes:
- TowelWarmerFilterSlugs
- Defined in:
- app/controllers/www/towel_warmers_controller.rb
Overview
Www::TowelWarmersController
Handles the towel warmer product grid with server-side Ransack filtering.
Supports SEO-friendly URL paths for filters:
/towel-warmer -> All products
/towel-warmer/brushed-gold -> Filter by finish
/towel-warmer/brushed-gold/round-bars -> Filter by finish + style
/towel-warmer/brushed-gold/round-bars/hardwired -> + connection
Constant Summary collapse
- PRODUCTS_PER_PAGE =
12- ALLOWED_SORTS =
['', 'effective_price asc', 'effective_price desc', 'title asc'].freeze
- DEFAULT_SORT =
'item_primary_product_line_slug_ltree asc, item_sku asc'- RETIRED_FINISH_SLUGS =
GET /towel-warmer
GET /towel-warmer/:filters (e.g., /towel-warmer/brushed-gold/round-bars)Main index action - displays all towel warmer products with optional filtering.
Filter segments from the URL path are parsed into Ransack parameters.
Products are paginated with Pagy; subsequent pages load via Turbo Stream.
Slugs that have been consolidated into composite pages and must 301. %w[polished-gold brushed-gold].freeze
Constants included from TowelWarmerFilterSlugs
TowelWarmerFilterSlugs::COMPOSITE_FINISH_SLUGS, TowelWarmerFilterSlugs::CONNECTION_LABELS, TowelWarmerFilterSlugs::CONNECTION_SLUGS, TowelWarmerFilterSlugs::FINISH_SLUGS, TowelWarmerFilterSlugs::MOUNTING_LABELS, TowelWarmerFilterSlugs::MOUNTING_SLUGS, TowelWarmerFilterSlugs::RANSACK_TO_SLUG, TowelWarmerFilterSlugs::SIZE_LABELS, TowelWarmerFilterSlugs::SIZE_SLUGS, TowelWarmerFilterSlugs::SLUG_TO_RANSACK, TowelWarmerFilterSlugs::STYLE_LABELS, TowelWarmerFilterSlugs::STYLE_SLUGS
Constants included from Controllers::MasqueradeGuarded
Controllers::MasqueradeGuarded::DEFAULT_BLOCK_MESSAGE
Constants included from Controllers::AnalyticsEvents
Controllers::AnalyticsEvents::MAX_QUEUED_EVENTS, Controllers::AnalyticsEvents::SESSION_KEY
Constants included from Controllers::ErrorRendering
Controllers::ErrorRendering::NON_CONTENT_PATH_PREFIXES
Constants included from SeoHelper
SeoHelper::AWARDS, SeoHelper::CA_ADDRESS, SeoHelper::CA_BUSINESS_HOURS, SeoHelper::CA_CONTACT_POINT, SeoHelper::CA_CURRENCIES, SeoHelper::CA_DESCRIPTION, SeoHelper::CA_FOUNDING_DATE, SeoHelper::CA_GLOBAL_LOCATION_NUMBER, SeoHelper::CA_LEGAL_NAME, SeoHelper::CA_LOCAL_BUSINESS, SeoHelper::CA_ONLINE_STORE, SeoHelper::CA_RETURN_POLICY, SeoHelper::CA_SALES_DEPARTMENT, SeoHelper::CA_SERVICE_AREA, SeoHelper::CA_URL, SeoHelper::CA_VAT_ID, SeoHelper::CA_WAREHOUSE_DEPARTMENT, SeoHelper::CA_WAREHOUSE_HOURS, SeoHelper::COMPANY_EMAIL, SeoHelper::COMPANY_LOGO, SeoHelper::COMPANY_NAME, SeoHelper::COMPANY_SLOGAN, SeoHelper::EXPERTISE, SeoHelper::FAX_NUMBER, SeoHelper::GS1_COMPANY_PREFIX, SeoHelper::ISO6523_CODE, SeoHelper::PAYMENT_METHODS, SeoHelper::PHONE_NUMBER, SeoHelper::PRIMARY_NAICS, SeoHelper::REFUND_TYPE, SeoHelper::RETURN_FEES, SeoHelper::RETURN_METHOD, SeoHelper::RETURN_POLICY_CATEGORY, SeoHelper::SECONDARY_NAICS, SeoHelper::SOCIAL_PROFILES, SeoHelper::US_ADDRESS, SeoHelper::US_BUSINESS_HOURS, SeoHelper::US_CONTACT_POINT, SeoHelper::US_CURRENCIES, SeoHelper::US_DESCRIPTION, SeoHelper::US_FOUNDING_DATE, SeoHelper::US_GLOBAL_LOCATION_NUMBER, SeoHelper::US_IMAGE, SeoHelper::US_LEGAL_NAME, SeoHelper::US_LOCAL_BUSINESS, SeoHelper::US_ONLINE_STORE, SeoHelper::US_RETURN_POLICY, SeoHelper::US_SALES_DEPARTMENT, SeoHelper::US_SERVICE_AREA, SeoHelper::US_TAX_ID, SeoHelper::US_URL, SeoHelper::US_WAREHOUSE_DEPARTMENT, SeoHelper::US_WAREHOUSE_HOURS
Constants included from IconHelper
IconHelper::CUSTOM_ICON_MAP, IconHelper::CUSTOM_SVG_DIR, IconHelper::DEFAULT_FAMILY
Instance Method Summary collapse
- #index ⇒ Object
-
#sort_with_top_sellers_first(skus) ⇒ Object
Sort SKUs to put top sellers first while preserving the rest of the order Uses a single SQL query to check for top-seller tags.
Methods included from TowelWarmerFilterSlugs
available_filter_counts, build_filter_path, filter_options_with_slugs, parse_filter_segments, size_filter_counts
Methods inherited from BasePortalController
#current_ability, #portal_party, #set_catalog, #set_webpack
Methods included from Controllers::MasqueradeGuarded
block_while_masquerading, #masquerade_blocks?
Methods inherited from ApplicationController
#account_impersonated?, #add_to_flash, #append_token, #bypass_forgery_protection?, #chat_enabled?, #cloudflare_cleared?, #default_catalog, #default_url_options, #enable_turbo_frames, #find_publication, #fix_invalid_accept_header, #init_js_utils, #is_globals_call?, #layout_by_resource, #locale_store, #redirect_to, #require_employee_for_crm, #set_base_host, #set_real_ip, #set_report_errors_for, #should_render_layout?, #stamp_impersonation_context, #warmlyyours_canada_ip?, #warmlyyours_ip?, #y
Methods included from Controllers::ReturnPathHandling
#check_for_return_path, #redirect_to_return_path_or_default
Methods included from Controllers::AnalyticsEvents
#consume_queued_analytics_events, #track_event
Methods included from Controllers::DeviceDetection
Methods included from Controllers::SubdomainDetection
#is_crm_request?, #is_www_request?, #json_request?
Methods included from Controllers::TrackingDetection
#bot_request?, #gdpr_country?, #gdpr_country_data, #prevent_bots, #set_tracking_cookie, #track_visitor?
Methods included from Controllers::AcceleratedFileSending
#send_file_accelerated, #send_upload_accelerated
Methods included from Controllers::ErrorRendering
#excp_string, #mail_to_for_error_reporting, #render_400, #render_404, #render_406, #render_410, #render_500, #render_invalid_authenticity_token, #render_ip_spoof_error, #safe_referer_or_fallback
Methods included from Controllers::TurnstileVerification
#load_turnstile_script_tag, #turnstile_lazy_widget, #turnstile_script_tag, #turnstile_widget, #validate_turnstile!
Methods included from Controllers::CloudflareCaching
edge_cached, #edge_cached_action?, #reset_cloudflare_cache, #set_cloudflare_cache, #skip_session
Methods included from Controllers::Webpackable
#preload_webpack_fonts, #webpack_css_include, #webpack_css_url, #webpack_js_include, #wpd_is_running?
Methods included from Controllers::Localizable
#cloudflare_country_locale, #determine_request_locale, #geocoder_locale, #guest_user_locale_check, #locale_optional_www_auth_path?, #param_locale, #set_locale, #set_request_locale, #skip_localization?, #warmlyyours_ip_locale
Methods included from Controllers::Authenticable
#access_denied, #authenticate_account, #authenticate_account!, #authenticate_account_from_login_token!, #authenticate_account_from_token!, #check_is_a_manager, #check_is_a_sales_manager, #check_is_an_admin, #check_is_an_employee, #check_party, #clear_mismatched_guest_user, #create_guest_user, #credentials?, #current_or_guest_user, #current_or_guest_user_id_read_only, #current_user, #devise_mapping, #fully_logged_in?, #generate_bot_id, #guest_user, #identifiable?, #init_current_user, #initialize_guest, #load_context_user, #logging_in, #resource, #resource_name, #restrict_access_for_non_employees, #scrubbed_request_path, #user_object, #warn_on_session_guest_id_leak
Methods included from ApplicationHelper
#better_number_to_currency, #check_force_logout, #check_or_cross, #check_or_times, #error_messages, #general_disclaimer_on_product_installation_and_local_codes, #gridjs_from_html_table, #gridjs_table, #is_wy_ip, #line_break, #parent_layout, #pass_or_fail, #render_error_messages_list, #render_video_card, #resolved_auth_form_turbo_frame, #return_path_or, #safe_css_color, #set_return_path_if_present, #set_section_if_present, #tab_frame_id, #to_underscore, #track_page?, #turbo_section_wrapper, #turbo_tabs_request?, #url_on_same_domain_as_request, #widget_index_daily_focus_index_path, #working_hours?, #yes_or_no, #yes_or_no_highlighted, #yes_or_no_with_check_or_cross, #youtube_video
Methods included from UppyUploaderHelper
#file_uploader, #image_uploader, #large_file_uploader_s3, #lead_sketch_uploader, #rma_image_uploader, #rma_image_uploader_s3, #uppy_uploader, #video_uploader
Methods included from ImagesHelper
#image_asset_tag, #image_asset_url
Methods included from SeoHelper
#add_page_schema, #canada?, #company_social_links, #ensure_context_json, #json_ld_script_tag, #local_business_schema, #online_store_id, #online_store_schema, #page_main_entity, #page_main_entity_json, #render_auto_collection_page_schema, #render_collection_page_schema, #render_local_business_schema, #render_online_store_schema, #render_page_schemas, #render_page_video_schemas, #render_webpage_schema, #render_webpage_schema_with_collections, #usa?
Methods included from UrlsHelper
#catalog_breadcrumb_links, #catalog_link, #catalog_link_for_product_line, #catalog_link_for_sku, #cms_link, #delocalized_path, #path_to_sales_product_sku, #path_to_sales_product_sku_for_product_line, #path_to_sales_product_sku_for_product_line_slug, #product_line_from_catalog_link, #protocol_neutral_url, #sanitize_external_url, #valid_external_url?
Methods included from IconHelper
#account_nav_icon, #fa_icon, #star_rating_html
Instance Method Details
#index ⇒ Object
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'app/controllers/www/towel_warmers_controller.rb', line 40 def index if (redirect_path = retired_slug_redirect) return redirect_to redirect_path, status: :moved_permanently end @q = base_scope.ransack(combined_filter_params) @q.sorts = default_sort if @q.sorts.empty? @products = @q.result all_skus = @products.pluck(:item_sku).uniq @skus = current_sort.blank? ? sort_with_top_sellers_first(all_skus) : all_skus # 301-redirect multi-filter pages that yield zero products to their parent. # e.g. /towel-warmer/brushed-gold/freestanding (no products) → /towel-warmer/brushed-gold if @skus.empty? && active_filters.size > 1 segments = params[:filters].to_s.split('/').reject(&:blank?) parent_segments = segments[0..-2] redirect_path = parent_segments.any? ? "/#{I18n.locale}/towel-warmer/#{parent_segments.join('/')}" : www_towel_warmers_path return redirect_to redirect_path, status: :moved_permanently end @total_product_count = @skus.size @pagy, @page_skus = pagy(:offset, @skus, limit: PRODUCTS_PER_PAGE) @page_skus_with_badges = build_connection_badges(@page_skus) # Track if filters are active - used to skip expensive content queries in view @filters_active = active_filters.present? respond_to do |format| format.html do set_cloudflare_cache(time_in_secs: 4.hours.to_i, tags: %w[towel-warmer product]) fresh_when(etag: [@products, combined_filter_params, current_sort, I18n.locale], last_modified: @products.maximum(:updated_at), public: true) end format.turbo_stream do return unless stale?(etag: [@products, combined_filter_params, current_sort, I18n.locale, @pagy.page], last_modified: @products.maximum(:updated_at), public: false) render partial: 'infinite_scroll' end end end |
#sort_with_top_sellers_first(skus) ⇒ Object
Sort SKUs to put top sellers first while preserving the rest of the order
Uses a single SQL query to check for top-seller tags
86 87 88 89 90 91 92 93 94 95 |
# File 'app/controllers/www/towel_warmers_controller.rb', line 86 def sort_with_top_sellers_first(skus) return skus if skus.empty? # Single query to get all SKUs that have the top-seller tag top_seller_skus = Item.('top-seller').where(sku: skus).pluck(:sku).to_set # Partition into top sellers and others, preserving original order within each group top_sellers, others = skus.partition { |sku| top_seller_skus.include?(sku) } top_sellers + others end |