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.
147 148 149 150 151 |
# File 'app/services/crm_navbar_counts.rb', line 147 def all_unread_email return unless manager? Activity.open_activities.inbound_emails.where.not(communication_id: nil).size end |
#all_unread_sms ⇒ Object
83 84 85 |
# File 'app/services/crm_navbar_counts.rb', line 83 def all_unread_sms SmsMessage.inbound.unread.size if manager? end |
#all_unread_voicemails ⇒ Object
114 115 116 |
# File 'app/services/crm_navbar_counts.rb', line 114 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.
160 161 162 |
# File 'app/services/crm_navbar_counts.rb', line 160 def communications_total sms_total + voicemail_total + unread_email end |
#global_sms_numbers ⇒ Object
91 92 93 |
# File 'app/services/crm_navbar_counts.rb', line 91 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.
191 192 193 194 195 |
# File 'app/services/crm_navbar_counts.rb', line 191 def manager? return @manager unless @manager.nil? @manager = user.try(:account).try(:is_manager?).to_b end |
#monitor_group_sms_numbers? ⇒ Boolean
95 96 97 |
# File 'app/services/crm_navbar_counts.rb', line 95 def monitor_group_sms_numbers? user.monitor_group_sms_numbers end |
#monitor_unlinked_extensions? ⇒ Boolean
118 119 120 |
# File 'app/services/crm_navbar_counts.rb', line 118 def monitor_unlinked_extensions? user.monitor_unlinked_extensions end |
#my_sms_numbers ⇒ Object
87 88 89 |
# File 'app/services/crm_navbar_counts.rb', line 87 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.
172 173 174 175 176 177 178 |
# File 'app/services/crm_navbar_counts.rb', line 172 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.
64 65 66 67 68 69 70 |
# File 'app/services/crm_navbar_counts.rb', line 64 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.
135 136 137 138 139 140 141 142 |
# File 'app/services/crm_navbar_counts.rb', line 135 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
79 80 81 |
# File 'app/services/crm_navbar_counts.rb', line 79 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.
75 76 77 |
# File 'app/services/crm_navbar_counts.rb', line 75 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".
125 126 127 128 129 |
# File 'app/services/crm_navbar_counts.rb', line 125 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.
104 105 106 107 108 109 110 111 112 |
# File 'app/services/crm_navbar_counts.rb', line 104 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.
183 184 185 |
# File 'app/services/crm_navbar_counts.rb', line 183 def watched_employee_ids @watched_employee_ids ||= user.effective_watch_list_ids end |