Module: Crm::RetailersHelper

Defined in:
app/helpers/crm/retailers_helper.rb

Overview

View helpers for the /retailers dashboards: the deep-linked metric cells
(shared data mapping with the compliance email via
Retailer::DailyComplianceReport.query_params_for), health scoring, and the
CatalogDataPoint-backed trend badges.

Constant Summary collapse

COLUMN_LABELS =

Column header labels, in COUNT_COLUMNS order.

{
  active_public: 'Active & Public',
  map_violations: 'MAP Violations',
  promotions: 'Promotions',
  price_diverging: 'Price Diverging',
  out_of_stock_active: 'Out of Stock (our inv.)',
  listing_issues: 'Listing Issues',
  scrape_failures: 'Scrape Failures',
  unreachable_online: 'Unreachable Online',
  out_of_stock_online: 'Out of Stock Online'
}.freeze
DANGER_METRICS =

Metrics coloured red when non-zero (a problem, not just informational).

%i[map_violations price_diverging out_of_stock_active listing_issues
scrape_failures unreachable_online out_of_stock_online].freeze

Instance Method Summary collapse

Instance Method Details

#amazon_sales_rank_change(change) ⇒ ActiveSupport::SafeBuffer

Accessible 30-day movement label for an Amazon sales rank. Rank is
inverted: moving from #100 to #70 is a 30-position improvement.

Parameters:

  • change (Integer, nil)

Returns:

  • (ActiveSupport::SafeBuffer)


114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'app/helpers/crm/retailers_helper.rb', line 114

def amazon_sales_rank_change(change)
  if change.nil?
    tag.span('No 30-day baseline', class: 'text-body-secondary small')
  elsif change.positive?
    tag.span(class: 'text-success text-nowrap') do
      safe_join([fa_icon('arrow-up'), " Improved #{number_with_delimiter(change)}"])
    end
  elsif change.negative?
    tag.span(class: 'text-danger text-nowrap') do
      safe_join([fa_icon('arrow-down'), " Worsened #{number_with_delimiter(change.abs)}"])
    end
  else
    tag.span('Unchanged', class: 'text-body-secondary text-nowrap')
  end
end

#health_class(pct) ⇒ String

Bootstrap contextual class for a health percentage.

Parameters:

  • pct (Integer)

    health percentage, 0..100 (see #retailer_health)

Returns:

  • (String)

    'success', 'warning', or 'danger'



73
74
75
76
77
78
79
# File 'app/helpers/crm/retailers_helper.rb', line 73

def health_class(pct)
  case pct
  when 90..100 then 'success'
  when 70..89  then 'warning'
  else 'danger'
  end
end

#metric_trend_badge(catalog, metric) ⇒ ActiveSupport::SafeBuffer?

Trend badge for one metric from CatalogDataPoint history: an arrow in the
direction the raw count moved, coloured by whether that's an improvement.
Returns nil until at least two data points exist.

Parameters:

  • catalog (Catalog)
  • metric (Symbol)

Returns:

  • (ActiveSupport::SafeBuffer, nil)


96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'app/helpers/crm/retailers_helper.rb', line 96

def metric_trend_badge(catalog, metric)
  cmp = catalog.data_points.period_comparison(metric)
  return if cmp.nil? || cmp[:change_percent].to_f.zero?

  rose = cmp[:change_percent].to_f.positive?
  inverted = CatalogDataPoint::INVERTED_METRICS.include?(metric.to_s)
  good = rose ? !inverted : inverted
  icon = rose ? 'arrow-up' : 'arrow-down'

  tag.span(class: "small #{good ? 'text-success' : 'text-danger'}") do
    safe_join([fa_icon(icon, family: :solid), " #{cmp[:change_percent].abs}%"])
  end
end

#retailer_health(row) ⇒ Integer

% of active items with no problem state (health gauge / column).

Parameters:

  • row (Hash)

Returns:

  • (Integer)

    0..100



62
63
64
65
66
67
68
# File 'app/helpers/crm/retailers_helper.rb', line 62

def retailer_health(row)
  active = row[:active_public].to_i
  return 100 if active.zero?

  pct = (1.0 - (row[:problem_items].to_i.to_f / active)) * 100
  pct.round.clamp(0, 100)
end

#retailer_metric_cell(row, metric) ⇒ ActiveSupport::SafeBuffer

A numeric table cell: links a positive count to its filtered CRM list
(ProductCatalogSearch, or the Listing Issues dashboard), red when it's a
non-zero problem metric.

Parameters:

  • row (Hash)

    a DailyComplianceReport row

  • metric (Symbol)

Returns:

  • (ActiveSupport::SafeBuffer)


33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'app/helpers/crm/retailers_helper.rb', line 33

def retailer_metric_cell(row, metric)
  val = row[metric].to_i
  danger = val.positive? && DANGER_METRICS.include?(metric)
  url = val.positive? ? retailer_metric_url(row[:catalog_id], metric) : nil
  classes = ['text-end', 'font-monospace', ('text-danger fw-semibold' if danger)].compact.join(' ')

  inner = if url
            link_to(val, url, class: danger ? 'text-danger' : nil)
          else
            val.to_s
          end
  tag.td(inner, class: classes)
end

#retailer_metric_url(catalog_id, metric) ⇒ String?

Deep-link path for a metric's filtered list, or nil.

Parameters:

  • catalog_id (Integer)

    the catalog (retailer) to scope the list to

  • metric (Symbol)

    the metric key (see COLUMN_LABELS)

Returns:

  • (String, nil)

    the filtered list path, or nil when the metric has no list



51
52
53
54
55
56
57
# File 'app/helpers/crm/retailers_helper.rb', line 51

def retailer_metric_url(catalog_id, metric)
  if metric == :listing_issues
    listing_issues_path(catalog_id:, status: 'open')
  elsif (qp = Retailer::DailyComplianceReport.query_params_for(metric, catalog_id))
    execute_search_path(type: 'ProductCatalogSearch', search: { query_params: qp })
  end
end

#retailer_type_badge(row) ⇒ ActiveSupport::SafeBuffer

1P/3P badge.

Parameters:

  • row (Hash)

    a DailyComplianceReport row (uses :retailer_type)

Returns:

  • (ActiveSupport::SafeBuffer)

    a Bootstrap badge span



84
85
86
87
88
# File 'app/helpers/crm/retailers_helper.rb', line 84

def retailer_type_badge(row)
  vendor = row[:retailer_type] == 'vendor'
  tag.span(vendor ? '1P' : '3P',
           class: "badge #{vendor ? 'bg-primary-subtle text-primary-emphasis' : 'bg-secondary-subtle text-secondary-emphasis'}")
end