Module: Crm::QueueCoveragesHelper

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

Overview

View helper: queue coverage dashboard.

Constant Summary collapse

HEALTH_TIERS =

One health vocabulary for the whole page, so the picker toggle and the gauge
over the grid can never disagree about a queue. Each tier pairs its colour
with an icon and a word — nothing here relies on colour alone.

{
  healthy: { css: 'success', icon: 'circle-check', label: 'Fully covered' },
  warning: { css: 'warning', icon: 'triangle-exclamation', label: 'Coverage gap' },
  critical: { css: 'danger', icon: 'circle-xmark', label: 'Low coverage' }
}.freeze
UNRATED =

A queue with no roster at all has nothing to rate — it reads as unknown
rather than as a failing queue, because the fix is queue membership in the
phone system, not staffing.

{ css: 'secondary', icon: 'circle-question', label: 'No roster' }.freeze

Instance Method Summary collapse

Instance Method Details

#queue_health_tier(coverage) ⇒ Hash

Returns the tier's css suffix, icon and label.

Parameters:

Returns:

  • (Hash)

    the tier's css suffix, icon and label



21
22
23
24
25
26
# File 'app/helpers/crm/queue_coverages_helper.rb', line 21

def queue_health_tier(coverage)
  # blank? routes through the delegated empty?, so a rosterless queue is unrated.
  return UNRATED if coverage.blank?

  HEALTH_TIERS.fetch(coverage.health)
end

#queue_option_label(queue, activity, member_counts) ⇒ String

Selector label carrying enough signal to spot a dead or unstaffed queue
without opening it — the registry is hand-maintained and a queue can take
calls while having no roster at all.

Parameters:

  • queue (PhoneQueue)
  • activity (Hash{Integer => Hash})

    from PhoneQueue.activity

  • member_counts (Hash{Integer => Integer})

    from PhoneQueue.member_counts

Returns:

  • (String)


87
88
89
90
91
92
93
94
95
96
97
98
# File 'app/helpers/crm/queue_coverages_helper.rb', line 87

def queue_option_label(queue, activity, member_counts)
  calls = activity.dig(queue., :calls).to_i
  members = member_counts.fetch(queue., 0)
  suffix = if calls.zero?
             'no recent calls'
           elsif members.zero?
             "#{number_with_delimiter(calls)} calls/90d, no roster"
           else
             "#{members} on roster, #{number_with_delimiter(calls)} calls/90d"
           end
  "#{queue.name} (#{queue.extension}) — #{suffix}"
end

#queue_toggle_title(queue, coverage, activity, member_counts) ⇒ String

Hover text for a queue toggle: the rating, why it earned it, and the roster
and call volume that used to sit in the select label.

Parameters:

  • queue (PhoneQueue)
  • coverage (QueueCoverage, nil)
  • activity (Hash{Integer => Hash})

    from PhoneQueue.activity

  • member_counts (Hash{Integer => Integer})

    from PhoneQueue.member_counts

Returns:

  • (String)


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

def queue_toggle_title(queue, coverage, activity, member_counts)
  tier = queue_health_tier(coverage)
  reason = coverage.presence&.health_reason

  [
    "#{queue.name}#{tier[:label]}",
    reason,
    queue_option_label(queue, activity, member_counts)
  ].compact_blank.join("\n")
end

#timezone_label(zone) ⇒ String

Returns the friendly Rails label, falling back to the raw name.

Parameters:

  • zone (String)

    IANA identifier

Returns:

  • (String)

    the friendly Rails label, falling back to the raw name



75
76
77
# File 'app/helpers/crm/queue_coverages_helper.rb', line 75

def timezone_label(zone)
  ActiveSupport::TimeZone[zone]&.to_s.presence || zone
end

#timezone_options(current = nil) ⇒ Array<Array(String, String)>

Rails' own time_zone_select emits friendly names ("Central Time (US &
Canada)") as values, but scheduler_profiles.timezone stores IANA
identifiers, and this select has to round-trip against that column. So the
label stays friendly and the value is the IANA name.

Several Rails zones share one IANA identifier (all four US mainland zones
have their own, but e.g. Edinburgh and London are both Europe/London), which
would otherwise emit duplicate options that cannot be told apart.

+current+ is folded in because ActiveSupport ships a curated subset of the
IANA database, not all of it. A zone persisted on a scheduler profile, or
remembered in the cookie, can be absent from that list — the select would
then render with nothing chosen and silently reassign the rep's zone on the
next submit.

Parameters:

  • current (String, nil) (defaults to: nil)

    the zone in force, always kept selectable

Returns:

  • (Array<Array(String, String)>)

    label/value pairs for options_for_select



64
65
66
67
68
69
70
71
# File 'app/helpers/crm/queue_coverages_helper.rb', line 64

def timezone_options(current = nil)
  names = ActiveSupport::TimeZone.all.map { |zone| zone.tzinfo.name }
  names << current if current.present?

  # "Paris — Europe/Paris". A zone ActiveSupport does not know has no friendly
  # name to pair, and uniq collapses the label to the identifier alone.
  names.uniq.map { |name| [[timezone_label(name), name].uniq.join(''), name] }.sort_by(&:first)
end