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)


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

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



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

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



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

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


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

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
# 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}")
  content << ('div', raw('<hr /><hr /><hr />'), 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



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

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

#filtering_params_cache_keyObject



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

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

#flash_messages(opts = {}) ⇒ Object



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

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



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

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



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

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



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

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


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

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



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

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


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

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



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

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



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

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


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

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



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

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

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



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

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