Module: Spree::FrontendHelper

Includes:
InlineSvg::ActionView::Helpers
Included in:
ProductsController, TaxonsController
Defined in:
app/helpers/spree/frontend_helper.rb

Instance Method Summary collapse

Instance Method Details

#asset_exists?(path) ⇒ Boolean

Returns:

  • (Boolean)


139
140
141
142
143
144
145
# File 'app/helpers/spree/frontend_helper.rb', line 139

def asset_exists?(path)
  if Rails.env.production?
    Rails.application.assets_manifest.find_sources(path).present?
  else
    Rails.application.assets.find_asset(path).present?
  end
end

#available_option_typesObject



250
251
252
253
254
255
# File 'app/helpers/spree/frontend_helper.rb', line 250

def available_option_types
  @available_option_types ||= Rails.cache.fetch("available-option-types/#{available_option_types_cache_key}") do
    Spree::OptionType.includes(:option_values).to_a
  end
  @available_option_types
end

#available_option_types_cache_keyObject



246
247
248
# File 'app/helpers/spree/frontend_helper.rb', line 246

def available_option_types_cache_key
  @available_option_types_cache_key ||= Spree::OptionType.maximum(:updated_at)&.utc&.to_i
end

#body_classObject



5
6
7
8
# File 'app/helpers/spree/frontend_helper.rb', line 5

def body_class
  @body_class ||= content_for?(:sidebar) ? 'two-col' : 'one-col'
  @body_class
end


174
175
176
177
178
179
180
181
182
183
184
185
# File 'app/helpers/spree/frontend_helper.rb', line 174

def carousel_image_source_set(image)
  return '' unless image

  widths = { lg: 1200, md: 992, sm: 768, xs: 576 }
  set = []
  widths.each do |key, value|
    file = main_app.url_for(image.url("plp_and_carousel_#{key}"))

    set << "#{file} #{value}w"
  end
  set.join(', ')
end

#checkout_progress(numbers: false) ⇒ Object



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'app/helpers/spree/frontend_helper.rb', line 68

def checkout_progress(numbers: false)
  states = @order.checkout_steps - ['complete']
  items = states.each_with_index.map do |state, i|
    text = Spree.t("order_state.#{state}").titleize
    text.prepend("#{i.succ}. ") if numbers

    css_classes = ['text-uppercase nav-item']
    current_index = states.index(@order.state)
    state_index = states.index(state)

    if state_index < current_index
      css_classes << 'completed'
      link_content =  :span, nil, class: 'checkout-progress-steps-image checkout-progress-steps-image--full'
      link_content << text
      text = link_to(link_content, spree.checkout_state_path(state), class: 'd-flex flex-column align-items-center', method: :get)
    end

    css_classes << 'next' if state_index == current_index + 1
    css_classes << 'active' if state == @order.state
    css_classes << 'first' if state_index == 0
    css_classes << 'last' if state_index == states.length - 1
    # No more joined classes. IE6 is not a target browser.
    # Hack: Stops <a> being wrapped round previous items twice.
    if state_index < current_index
      ('li', text, class: css_classes.join(' '))
    else
      link_content = if state == @order.state
                        :span, nil, class: 'checkout-progress-steps-image checkout-progress-steps-image--full'
                     else
                       inline_svg_tag 'circle.svg', class: 'checkout-progress-steps-image'
                     end
      link_content << text
      ('li', ('a', link_content, class: "d-flex flex-column align-items-center #{'active' if state == @order.state}"), class: css_classes.join(' '))
    end
  end
  content = ('ul', raw(items.join("\n")), class: 'nav justify-content-between checkout-progress-steps', id: "checkout-step-#{@order.state}")
  hrs = '<hr />' * (states.length - 1)
  content << ('div', raw(hrs), class: "checkout-progress-steps-line state-#{@order.state}")
end

#class_for(flash_type) ⇒ Object



57
58
59
60
61
62
63
64
65
66
# File 'app/helpers/spree/frontend_helper.rb', line 57

def class_for(flash_type)
  {
    success: 'success',
    registration_error: 'danger',
    error: 'danger',
    alert: 'danger',
    warning: 'warning',
    notice: 'success'
  }[flash_type.to_sym]
end

#filtering_paramsObject



238
239
240
# File 'app/helpers/spree/frontend_helper.rb', line 238

def filtering_params
  @filtering_params ||= available_option_types.map(&:filter_param).concat(static_filters)
end

#filtering_params_cache_keyObject



242
243
244
# File 'app/helpers/spree/frontend_helper.rb', line 242

def filtering_params_cache_key
  @filtering_params_cache_key ||= params.permit(*filtering_params)&.reject { |_, v| v.blank? }&.to_param
end

#flash_messages(opts = {}) ⇒ Object



108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'app/helpers/spree/frontend_helper.rb', line 108

def flash_messages(opts = {})
  flashes = ''
  excluded_types = opts[:excluded_types].to_a.map(&:to_s)

  flash.to_h.except('order_completed').each do |msg_type, text|
    next if msg_type.blank? || excluded_types.include?(msg_type)

    flashes << (:div, class: "alert alert-#{class_for(msg_type)} mb-0") do
      (:button, '&times;'.html_safe, class: 'close', data: { dismiss: 'alert', hidden: true }) +
        (:span, text)
    end
  end
  flashes.html_safe
end

#icon(name:, classes: '', width:, height:) ⇒ Object



220
221
222
# File 'app/helpers/spree/frontend_helper.rb', line 220

def icon(name:, classes: '', width:, height:)
  inline_svg_tag "#{name}.svg", class: "spree-icon #{classes}", size: "#{width}px*#{height}px"
end

#image_source_set(name) ⇒ Object



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'app/helpers/spree/frontend_helper.rb', line 187

def image_source_set(name)
  widths = {
    desktop: '1200',
    tablet_landscape: '992',
    tablet_portrait: '768',
    mobile: '576'
  }
  set = []
  widths.each do |key, value|
    filename = key == :desktop ? name : "#{name}_#{key}"
    file = asset_path("#{filename}.jpg")

    set << "#{file} #{value}w"
  end
  set.join(', ')
end

#lazy_image(src:, alt:, width:, height:, srcset: '', **options) ⇒ Object



162
163
164
165
166
167
# File 'app/helpers/spree/frontend_helper.rb', line 162

def lazy_image(src:, alt:, width:, height:, srcset: '', **options)
  # We need placeholder image with the correct size to prevent page from jumping
  placeholder = "data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20#{width}%20#{height}'%3E%3C/svg%3E"

  image_tag placeholder, data: { src: src, srcset: srcset }, class: "#{options[:class]} lazyload", alt: alt
end


123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'app/helpers/spree/frontend_helper.rb', line 123

def link_to_cart(text = nil)
  text = text ? h(text) : Spree.t('cart')
  css_class = nil

  if simple_current_order.nil? || simple_current_order.item_count.zero?
    text = "<span class='glyphicon glyphicon-shopping-cart'></span> #{text}: (#{Spree.t('empty')})"
    css_class = 'empty'
  else
    text = "<span class='glyphicon glyphicon-shopping-cart'></span> #{text}: (#{simple_current_order.item_count})
            <span class='amount'>#{simple_current_order.display_total.to_html}</span>"
    css_class = 'full'
  end

  link_to text.html_safe, spree.cart_path, class: "cart-info nav-link #{css_class}"
end

#permitted_product_paramsObject



169
170
171
172
# File 'app/helpers/spree/frontend_helper.rb', line 169

def permitted_product_params
  product_filters = available_option_types.map(&:name)
  params.permit(product_filters << :sort_by)
end


147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'app/helpers/spree/frontend_helper.rb', line 147

def plp_and_carousel_image(product, image_class = '')
  image = default_image_for_product_or_variant(product)
  image_url = image&.plp_url || asset_path('noimage/plp.png')
  image_style = image&.style(:plp)

  lazy_image(
    src: image_url,
    srcset: carousel_image_source_set(image),
    alt: product.name,
    width: image_style&.dig(:width) || 278,
    height: image_style&.dig(:height) || 371,
    class: "product-component-image d-block mw-100 #{image_class}"
  )
end

#price_filter_valuesObject



224
225
226
227
228
229
230
231
232
# File 'app/helpers/spree/frontend_helper.rb', line 224

def price_filter_values
  [
    "#{I18n.t('activerecord.attributes.spree/product.less_than')} #{formatted_price(50)}",
    "#{formatted_price(50)} - #{formatted_price(100)}",
    "#{formatted_price(101)} - #{formatted_price(150)}",
    "#{formatted_price(151)} - #{formatted_price(200)}",
    "#{formatted_price(201)} - #{formatted_price(300)}"
  ]
end

#set_image_alt(image) ⇒ Object



216
217
218
# File 'app/helpers/spree/frontend_helper.rb', line 216

def set_image_alt(image)
  return image.alt if image.alt.present?
end

#spree_breadcrumbs(taxon, _separator = '', product = nil) ⇒ Object



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'app/helpers/spree/frontend_helper.rb', line 10

def spree_breadcrumbs(taxon, _separator = '', product = nil)
  return '' if current_page?('/') || taxon.nil?

  # breadcrumbs for root
  crumbs = [(:li, (
    :a, (
      :span, Spree.t(:home), itemprop: 'name'
    ) << (:meta, nil, itemprop: 'position', content: '0'), itemprop: 'url', href: spree.root_path
  ) << (:span, nil, itemprop: 'item', itemscope: 'itemscope', itemtype: 'https://schema.org/Thing', itemid: spree.root_path), itemscope: 'itemscope', itemtype: 'https://schema.org/ListItem', itemprop: 'itemListElement', class: 'breadcrumb-item')]

  if taxon
    ancestors = taxon.ancestors.where.not(parent_id: nil)

    # breadcrumbs for ancestor taxons
    crumbs << ancestors.each_with_index.map do |ancestor, index|
      (:li, (
        :a, (
          :span, ancestor.name, itemprop: 'name'
        ) << (:meta, nil, itemprop: 'position', content: index + 1), itemprop: 'url', href: seo_url(ancestor, params: permitted_product_params)
      ) << (:span, nil, itemprop: 'item', itemscope: 'itemscope', itemtype: 'https://schema.org/Thing', itemid: seo_url(ancestor, params: permitted_product_params)), itemscope: 'itemscope', itemtype: 'https://schema.org/ListItem', itemprop: 'itemListElement', class: 'breadcrumb-item')
    end

    # breadcrumbs for current taxon
    crumbs << (:li, (
      :a, (
        :span, taxon.name, itemprop: 'name'
      ) << (:meta, nil, itemprop: 'position', content: ancestors.size + 1), itemprop: 'url', href: seo_url(taxon, params: permitted_product_params)
    ) << (:span, nil, itemprop: 'item', itemscope: 'itemscope', itemtype: 'https://schema.org/Thing', itemid: seo_url(taxon, params: permitted_product_params)), itemscope: 'itemscope', itemtype: 'https://schema.org/ListItem', itemprop: 'itemListElement', class: 'breadcrumb-item')

    # breadcrumbs for product
    if product
      crumbs << (:li, (
        :span, (
          :span, product.name, itemprop: 'name'
        ) << (:meta, nil, itemprop: 'position', content: ancestors.size + 2), itemprop: 'url', href: spree.product_path(product, taxon_id: taxon&.id)
      ) << (:span, nil, itemprop: 'item', itemscope: 'itemscope', itemtype: 'https://schema.org/Thing', itemid: spree.product_path(product, taxon_id: taxon&.id)), itemscope: 'itemscope', itemtype: 'https://schema.org/ListItem', itemprop: 'itemListElement', class: 'breadcrumb-item')
    end
  else
    # breadcrumbs for product on PDP
    crumbs << (:li, (
      :span, Spree.t(:products), itemprop: 'item'
    ) << (:meta, nil, itemprop: 'position', content: '1'), class: 'active', itemscope: 'itemscope', itemtype: 'https://schema.org/ListItem', itemprop: 'itemListElement')
  end
  crumb_list = (:ol, raw(crumbs.flatten.map(&:mb_chars).join), class: 'breadcrumb', itemscope: 'itemscope', itemtype: 'https://schema.org/BreadcrumbList')
  (:nav, crumb_list, id: 'breadcrumbs', class: 'col-12 mt-1 mt-sm-3 mt-lg-4', aria: { label: 'breadcrumb' })
end


257
258
259
260
261
262
263
264
265
# File 'app/helpers/spree/frontend_helper.rb', line 257

def spree_social_link(service)
  return '' if current_store.send(service).blank?

  link_to "https://#{service}.com/#{current_store.send(service)}", target: :blank, rel: 'nofollow noopener' do
     :figure, id: service, class: 'px-2' do
      icon(name: service, width: 22, height: 22)
    end
  end
end

#static_filtersObject



234
235
236
# File 'app/helpers/spree/frontend_helper.rb', line 234

def static_filters
  @static_filters ||= Spree::Frontend::Config[:products_filters]
end

#taxons_tree(root_taxon, current_taxon, max_level = 1) ⇒ Object



204
205
206
207
208
209
210
211
212
213
214
# File 'app/helpers/spree/frontend_helper.rb', line 204

def taxons_tree(root_taxon, current_taxon, max_level = 1)
  return '' if max_level < 1 || root_taxon.leaf?

   :div, class: 'list-group' do
    taxons = root_taxon.children.map do |taxon|
      css_class = current_taxon&.self_and_ancestors&.include?(taxon) ? 'list-group-item list-group-item-action active' : 'list-group-item list-group-item-action'
      link_to(taxon.name, seo_url(taxon), class: css_class) + taxons_tree(taxon, current_taxon, max_level - 1)
    end
    safe_join(taxons, "\n")
  end
end