Class: Www::ProductsController
- Inherits:
-
BasePortalController
- Object
- ActionController::Base
- ApplicationController
- BasePortalController
- Www::ProductsController
- Defined in:
- app/controllers/www/products_controller.rb
Constant Summary
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
- #add_to_cart ⇒ Object
-
#bulk_product_data ⇒ JSON
Consolidated bulk product data endpoint Returns product data for one or more SKUs with field filtering.
- #cart_add_item(sku_code, quantity = nil, room_configuration_id = nil) ⇒ Object protected
-
#code ⇒ Object
Legacy route: 301 redirects to new hierarchical URL.
- #index ⇒ Object
-
#line ⇒ Object
Legacy route: 301 redirects to new hierarchical URL.
-
#reviews ⇒ Object
Legacy route: 301 redirects to new hierarchical URL.
-
#section ⇒ Object
Lazy-loaded section endpoint for below-the-fold content Renders a single section via Turbo Frame for performance.
Methods included from Controllers::TrackingDetection
#bot_request?, #gdpr_country?, #gdpr_country_data, #prevent_bots, #set_tracking_cookie, #track_visitor?
Methods inherited from BasePortalController
#current_ability, #portal_party, #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::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
#add_to_cart ⇒ Object
54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
# File 'app/controllers/www/products_controller.rb', line 54 def add_to_cart @cart = @context_user.cart @cart.save qty = params[:quantity].to_i.abs qty > 99 ? 99 : qty # Force to be max of 99 units to avoid issues cart_add_item(params[:sku], params[:quantity]) respond_to do |format| format.json do render json: { success: true } end end end |
#bulk_product_data ⇒ JSON
Consolidated bulk product data endpoint
Returns product data for one or more SKUs with field filtering
GET /products/bulk_product_data?skus[]=SKU1&skus[]=SKU2&fields=price
GET /products/bulk_product_data?skus[]=SKU1&fields=price,stock,shipping,cta
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
# File 'app/controllers/www/products_controller.rb', line 132 def bulk_product_data skus = Array(params[:skus]).compact.uniq.first(50) # Limit to 50 SKUs max if skus.empty? render json: { products: {}, has_tier_pricing: false, fetched_at: Time.current.iso8601 } return end # Parse requested fields - defaults to all if not specified requested_fields = if params[:fields].present? params[:fields].split(',').map(&:strip).map(&:downcase) else %w[price stock shipping cta] end # Get customer for tier pricing customer = current_account&.customer has_tier_pricing = customer&.pricing_program_discount.to_f.positive? # Fetch all products from the appropriate catalog catalog_id = Catalog.locale_to_catalog_id(I18n.locale) products_by_sku = ViewProductCatalog .where(catalog_id: catalog_id, item_sku: skus) .includes(:catalog_item) .preload(item: [ :primary_image, { image_profiles: :image } ]) .index_by(&:item_sku) # Build response for each SKU products_data = {} skus.each do |sku| vpc = products_by_sku[sku] next unless vpc # Set @pcp for consolidated_product_payload to use @pcp = Www::ProductCatalogPresenter.new(vpc, view_context) products_data[sku] = consolidated_product_payload(fields: requested_fields, customer: customer) end # Build cache key for HTTP caching # Include customer ID if they have tier pricing to make cache personalized cache_key = if has_tier_pricing "bulk_product_data/#{skus.sort.join(',')}/#{requested_fields.sort.join(',')}/customer/#{customer.id}" else "bulk_product_data/#{skus.sort.join(',')}/#{requested_fields.sort.join(',')}" end # Use HTTP caching with ETag return unless stale?(etag: cache_key, public: !has_tier_pricing) # Set cache time - private for tier pricing customers if has_tier_pricing expires_in 1.minute, private: true else expires_in 1.minute, public: true end render json: { products: products_data, has_tier_pricing: has_tier_pricing, program_name: has_tier_pricing ? customer.tier2_program_pricing&.title : nil, fetched_at: Time.current.iso8601 }.compact end |
#cart_add_item(sku_code, quantity = nil, room_configuration_id = nil) ⇒ Object (protected)
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 |
# File 'app/controllers/www/products_controller.rb', line 201 def cart_add_item(sku_code, quantity = nil, room_configuration_id = nil) # add item @recently_added_items ||= [] return if sku_code.blank? sku = sku_code quantity = quantity.presence quantity = (quantity || 1).to_i room_configuration_id = room_configuration_id return unless sku.present? and quantity > 0 # find catalog by locale ci = begin @cart.catalog.catalog_items.public_catalog_items.by_skus(sku).first rescue StandardError nil end if ci @cart.recalculate_shipping = true # doesn't hurt to set it @cart.recalculate_discounts = true # ensure tier2 and auto-apply discounts are calculated @cart.force_total_reset = true @new_line = @cart.add_line_item(catalog_item_id: ci.id, quantity: quantity, room_configuration_id: room_configuration_id, do_not_autosave: true) @recently_added_items << { id: @new_line.id, sku: @new_line.sku, name: @new_line.name, category: @new_line.reported_category_name, quantity: @new_line.quantity } else flash[:error] = "We could not find sku #{sku} in catalog." end end |
#code ⇒ Object
Legacy route: 301 redirects to new hierarchical URL
45 46 47 48 49 50 51 52 |
# File 'app/controllers/www/products_controller.rb', line 45 def code result = CatalogPathResolver.new.resolve_legacy_sku(params[:sku]) if result.redirect? redirect_to result.redirect_to, status: :moved_permanently else redirect_to cms_link('/products'), status: :moved_permanently end end |
#index ⇒ Object
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
# File 'app/controllers/www/products_controller.rb', line 7 def index @catalog = default_catalog @product_lines = @catalog.product_lines_for_sales_portal.reorder(:priority, :public_name, :slug_ltree) @h1 = 'All WarmlyYours Products' @page_title = "All WarmlyYours Products in #{Country.country_name_from_locale}" @page_description = 'Find all WarmlyYours products in a single page. Electric Radiant Floor Heating, Outdoor Snow Melting Products, Roof and Gutter Deicing Systems, Pipe Freeze Protection, Mirrors and Defoggers, Towel Warmers and Panel Heaters' @filters = [] b = { type: 'checkbox', title: 'Product Line', options: ['Floor Heating', 'Snow Melting', 'Towel Warmer', 'LED Mirror', 'Pipe Freeze Protection', 'Roof & Gutter Deicing', 'Radiant Panel', 'Countertop Heater', 'Underlayment', 'Third Party Control Integration'] } @filters << b set_cloudflare_cache(time_in_secs: 4.hours.to_i, tags: %w[sale product]) fresh_when(etag: [@product_lines, I18n.locale], last_modified: @product_lines.maximum(:updated_at), public: true) end |
#line ⇒ Object
Legacy route: 301 redirects to new hierarchical URL
35 36 37 38 39 40 41 42 |
# File 'app/controllers/www/products_controller.rb', line 35 def line result = CatalogPathResolver.new.resolve_legacy_product_line(params[:product_line_url]) if result.redirect? redirect_to result.redirect_to, status: :moved_permanently else redirect_to cms_link('/products'), status: :moved_permanently end end |
#reviews ⇒ Object
Legacy route: 301 redirects to new hierarchical URL
70 71 72 73 74 75 76 77 |
# File 'app/controllers/www/products_controller.rb', line 70 def reviews result = CatalogPathResolver.new.resolve_legacy_sku(params[:sku], section: :reviews) if result.redirect? redirect_to result.redirect_to, status: :moved_permanently else redirect_to cms_link('/products'), status: :moved_permanently end end |
#section ⇒ Object
Lazy-loaded section endpoint for below-the-fold content
Renders a single section via Turbo Frame for performance
81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'app/controllers/www/products_controller.rb', line 81 def section load_product(params[:sku], web_accessible_only: false, light: true) return head :not_found unless @product&.item_is_web_accessible @section_name = params[:section].to_sym @section_data = @pcp.compute_single_section(@section_name) @section_data = nil if @section_name == :related_products && @section_data.blank? @p = @pcp set_cloudflare_cache(time_in_secs: 4.hours.to_i, tags: %w[sale product]) fresh_when(etag: [@product, @section_name, I18n.locale], last_modified: @product.updated_at, public: true) end |