Class: CrmNavbarCounts
- Inherits:
-
Object
- Object
- CrmNavbarCounts
- Defined in:
- app/services/crm_navbar_counts.rb
Overview
Computes the badge counts shown in the CRM navbar for a given user. Single
source of truth shared by:
- the navbar partial (initial render)
- the badge fragment partials (replace targets for Turbo Stream broadcasts)
- the resync endpoint
Each method returns just the integer the corresponding badge displays, so the
partials stay dumb. Manager-only "all unread" counts and watch-list-derived
scopes live here too, again to keep the partials free of conditional logic.
Caching: the four totals fed to badge partials (sms_total, voicemail_total,
unread_email, pinned_total) are wrapped in a short-TTL Rails.cache fetch.
Initial CRM page renders re-hit these counts on every full response and the
underlying queries (joins on watch lists, full_name lookups, search-result
joins) were a top contributor to AppSignal performance incident #339 and
kin — the navbar partial alone showed up at 65–693ms per request. With a
30s window the badges stay near-fresh while page load drops a measurable
slice. Authoritative liveness is preserved because CrmNavbarRefreshWorker
busts these cache keys before broadcasting a replace, so the broadcast
payload always reflects the latest data.
Constant Summary collapse
- CACHE_TTL =
30.seconds
- CACHED_METHODS =
%i[sms_total voicemail_total unread_email pinned_total].freeze
Instance Attribute Summary collapse
-
#user ⇒ Object
readonly
Returns the value of attribute user.
Class Method Summary collapse
-
.bust_cache(user) ⇒ void
Drops the cached totals for a user so the next read recomputes.
-
.cache_key(user, method_name) ⇒ Array(String, Integer, Symbol)
Builds the Rails.cache key for one cached badge total.
Instance Method Summary collapse
-
#all_unread_email ⇒ Integer?
Compute the total unread inbound emails across the account for manager users.
- #all_unread_sms ⇒ Object
- #all_unread_voicemails ⇒ Object
-
#communications_total ⇒ Object
Communications (combined).
- #global_sms_numbers ⇒ Object
-
#initialize(user) ⇒ void
constructor
Create a CrmNavbarCounts instance for the given user.
-
#manager? ⇒ Boolean
Determine whether the current user is an account manager.
- #monitor_group_sms_numbers? ⇒ Boolean
- #monitor_unlinked_extensions? ⇒ Boolean
- #my_sms_numbers ⇒ Object
-
#pinned_total ⇒ Integer
Pinned items.
-
#sms_total ⇒ Integer
Total unread SMS messages to display in the user's CRM navbar.
-
#unread_email ⇒ Integer
Count unread inbound emails assigned to the user's watched employees, excluding records with no
communication_id. - #unread_global_sms ⇒ Object
-
#unread_my_sms ⇒ Integer
Count unread inbound SMS messages for the user's monitored SMS numbers.
-
#voicemail_destination_filter ⇒ Object
Mirrors what the voicemails-by-watch-list link expects: stringified watch list ids, optionally appended with the "null" sentinel that CallRecord#destination_filter recognises as "include unlinked extensions".
-
#voicemail_total ⇒ Integer
Computes the integer total of unread voicemails shown in the CRM navbar for the user.
-
#watched_employee_ids ⇒ Array<Integer>
List of employee IDs the user is watching.
Constructor Details
#initialize(user) ⇒ void
Create a CrmNavbarCounts instance for the given user.
31 32 33 |
# File 'app/services/crm_navbar_counts.rb', line 31 def initialize(user) @user = user end |
Instance Attribute Details
#user ⇒ Object (readonly)
Returns the value of attribute user.
35 36 37 |
# File 'app/services/crm_navbar_counts.rb', line 35 def user @user end |
Class Method Details
.bust_cache(user) ⇒ void
This method returns an undefined value.
Drops the cached totals for a user so the next read recomputes. Called by
CrmNavbarRefreshWorker before broadcasting a badge replace, so the live
update reflects the freshest data instead of whatever was cached when the
event fired.
44 45 46 47 48 |
# File 'app/services/crm_navbar_counts.rb', line 44 def self.bust_cache(user) return unless user CACHED_METHODS.each { |m| Rails.cache.delete(cache_key(user, m)) } end |
.cache_key(user, method_name) ⇒ Array(String, Integer, Symbol)
Builds the Rails.cache key for one cached badge total.
55 56 57 |
# File 'app/services/crm_navbar_counts.rb', line 55 def self.cache_key(user, method_name) ["crm_navbar_counts", user.id, method_name] end |
Instance Method Details
#all_unread_email ⇒ Integer?
Compute the total unread inbound emails across the account for manager users.
144 145 146 147 148 |
# File 'app/services/crm_navbar_counts.rb', line 144 def all_unread_email return unless manager? Activity.open_activities.inbound_emails.where.not(communication_id: nil).size end |
#all_unread_sms ⇒ Object
82 83 84 |
# File 'app/services/crm_navbar_counts.rb', line 82 def all_unread_sms SmsMessage.inbound.unread.size if manager? end |
#all_unread_voicemails ⇒ Object
112 113 114 |
# File 'app/services/crm_navbar_counts.rb', line 112 def all_unread_voicemails CallRecord.voicemails.unread.size if manager? end |
#communications_total ⇒ Object
Communications (combined)
Aggregate "unread for me" badge for the unified Communications offcanvas
in the navbar (sms + voicemails + email). Mirrors the per-channel _total
methods so the visual badge matches what the offcanvas surfaces in its
three sections. Each underlying call is already memoised through its own
query — this just sums the integers.
157 158 159 |
# File 'app/services/crm_navbar_counts.rb', line 157 def communications_total sms_total + voicemail_total + unread_email end |
#global_sms_numbers ⇒ Object
90 91 92 |
# File 'app/services/crm_navbar_counts.rb', line 90 def global_sms_numbers SmsMessage::SMS_GLOBAL_NUMBERS end |
#manager? ⇒ Boolean
Determine whether the current user is an account manager.
Memoizes the result for subsequent calls.
188 189 190 191 192 |
# File 'app/services/crm_navbar_counts.rb', line 188 def manager? return @manager unless @manager.nil? @manager = user.try(:account).try(:is_manager?).to_b end |
#monitor_group_sms_numbers? ⇒ Boolean
94 95 96 |
# File 'app/services/crm_navbar_counts.rb', line 94 def monitor_group_sms_numbers? user.monitor_group_sms_numbers end |
#monitor_unlinked_extensions? ⇒ Boolean
116 117 118 |
# File 'app/services/crm_navbar_counts.rb', line 116 def monitor_unlinked_extensions? user.monitor_unlinked_extensions end |
#my_sms_numbers ⇒ Object
86 87 88 |
# File 'app/services/crm_navbar_counts.rb', line 86 def my_sms_numbers @my_sms_numbers ||= Employee.all_active_employees_sms_numbers(rep_ids: watched_employee_ids) end |
#pinned_total ⇒ Integer
Pinned items
Counts the rows of the user's currently-pinned Search (if any). Each user
has at most one pinned Search at a time (Search#remove_other_pins enforces
Counts the current user's pinned search results.
The value is cached per user.
169 170 171 172 173 174 175 |
# File 'app/services/crm_navbar_counts.rb', line 169 def pinned_total cached(:pinned_total) do SearchResult.joins(:search) .where(searches: { employee_id: user.id, pinned: true }) .size end end |
#sms_total ⇒ Integer
Total unread SMS messages to display in the user's CRM navbar.
Includes unread inbound SMS for the user's watched numbers and also includes unread global SMS when the user has group SMS monitoring enabled.
63 64 65 66 67 68 69 |
# File 'app/services/crm_navbar_counts.rb', line 63 def sms_total cached(:sms_total) do count = unread_my_sms count += unread_global_sms if user.monitor_group_sms_numbers count end end |
#unread_email ⇒ Integer
Count unread inbound emails assigned to the user's watched employees, excluding records with no communication_id.
132 133 134 135 136 137 138 139 |
# File 'app/services/crm_navbar_counts.rb', line 132 def unread_email cached(:unread_email) do Activity.open_activities.inbound_emails .where(assigned_resource_id: watched_employee_ids) .where.not(communication_id: nil) .size end end |
#unread_global_sms ⇒ Object
78 79 80 |
# File 'app/services/crm_navbar_counts.rb', line 78 def unread_global_sms SmsMessage.for_numbers(SmsMessage::SMS_GLOBAL_NUMBERS).inbound.unread.size end |
#unread_my_sms ⇒ Integer
Count unread inbound SMS messages for the user's monitored SMS numbers.
74 75 76 |
# File 'app/services/crm_navbar_counts.rb', line 74 def unread_my_sms SmsMessage.for_numbers(my_sms_numbers).inbound.unread.size end |
#voicemail_destination_filter ⇒ Object
Mirrors what the voicemails-by-watch-list link expects: stringified watch
list ids, optionally appended with the "null" sentinel that
CallRecord#destination_filter recognises as "include unlinked extensions".
123 124 125 126 127 |
# File 'app/services/crm_navbar_counts.rb', line 123 def voicemail_destination_filter filter = watched_employee_ids.map(&:to_s) filter << 'null' if monitor_unlinked_extensions? filter end |
#voicemail_total ⇒ Integer
Computes the integer total of unread voicemails shown in the CRM navbar for the user.
The result is cached per user for 30 seconds.
102 103 104 105 106 107 108 109 110 |
# File 'app/services/crm_navbar_counts.rb', line 102 def voicemail_total cached(:voicemail_total) do if monitor_unlinked_extensions? CallRecord.voicemails.unread.for_destination_employees_or_unlinked(watched_employee_ids).size else CallRecord.voicemails.unread.for_destination_employees(watched_employee_ids).size end end end |
#watched_employee_ids ⇒ Array<Integer>
List of employee IDs the user is watching.
180 181 182 |
# File 'app/services/crm_navbar_counts.rb', line 180 def watched_employee_ids @watched_employee_ids ||= user.effective_watch_list_ids end |