mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-10-30 18:47:51 +00:00 
			
		
		
		
	 f1f1ce644c
			
		
	
	f1f1ce644c
	
	
	
		
			
			This PR adds inbox filtering to the conversation traffic heatmap, allowing users to analyze patterns for specific inboxes. Additionally, it also adds a new resolution count heatmap that shows when support teams are most active in resolving conversations, using a green color to distinguish it from the blue conversation heatmap. The PR also reorganizes heatmap components into a cleaner structure with a shared `BaseHeatmapContainer` that handles common functionality like date range selection, inbox filtering, and data fetching. This makes it easy to add new heatmap metrics in the future - just create a wrapper component specifying the metric type and color scheme. <img width="1926" height="1670" alt="CleanShot 2025-10-13 at 14 01 35@2x" src="https://github.com/user-attachments/assets/67822a34-6170-4d19-9e11-7ad4ded5c388" /> <img width="1964" height="1634" alt="CleanShot 2025-10-13 at 14 03 00@2x" src="https://github.com/user-attachments/assets/e4613c08-64b8-4fa6-91d8-7510946dd75d" /> Unrelated change, the data seeder conversation resolution would not work correctly, we've fixed it. --------- Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
		
			
				
	
	
		
			120 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
			
		
		
	
	
			120 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
| # frozen_string_literal: true
 | |
| 
 | |
| require 'faker'
 | |
| require 'active_support/testing/time_helpers'
 | |
| 
 | |
| class Seeders::Reports::ConversationCreator
 | |
|   include ActiveSupport::Testing::TimeHelpers
 | |
| 
 | |
|   def initialize(account:, resources:)
 | |
|     @account = account
 | |
|     @contacts = resources[:contacts]
 | |
|     @inboxes = resources[:inboxes]
 | |
|     @teams = resources[:teams]
 | |
|     @labels = resources[:labels]
 | |
|     @agents = resources[:agents]
 | |
|     @priorities = [nil, 'urgent', 'high', 'medium', 'low']
 | |
|   end
 | |
| 
 | |
|   # rubocop:disable Metrics/MethodLength
 | |
|   def create_conversation(created_at:)
 | |
|     conversation = nil
 | |
|     should_resolve = false
 | |
|     resolution_time = nil
 | |
| 
 | |
|     ActiveRecord::Base.transaction do
 | |
|       travel_to(created_at) do
 | |
|         conversation = build_conversation
 | |
|         conversation.save!
 | |
| 
 | |
|         add_labels_to_conversation(conversation)
 | |
|         create_messages_for_conversation(conversation)
 | |
| 
 | |
|         # Determine if should resolve but don't update yet
 | |
|         should_resolve = rand > 0.3
 | |
|         if should_resolve
 | |
|           resolution_delay = rand((30.minutes)..(24.hours))
 | |
|           resolution_time = created_at + resolution_delay
 | |
|         end
 | |
|       end
 | |
| 
 | |
|       travel_back
 | |
|     end
 | |
| 
 | |
|     # Now resolve outside of time travel if needed
 | |
|     if should_resolve && resolution_time
 | |
|       # rubocop:disable Rails/SkipsModelValidations
 | |
|       conversation.update_column(:status, :resolved)
 | |
|       conversation.update_column(:updated_at, resolution_time)
 | |
|       # rubocop:enable Rails/SkipsModelValidations
 | |
| 
 | |
|       # Trigger the event with proper timestamp
 | |
|       travel_to(resolution_time) do
 | |
|         trigger_conversation_resolved_event(conversation)
 | |
|       end
 | |
|       travel_back
 | |
|     end
 | |
| 
 | |
|     conversation
 | |
|   end
 | |
|   # rubocop:enable Metrics/MethodLength
 | |
| 
 | |
|   private
 | |
| 
 | |
|   def build_conversation
 | |
|     contact = @contacts.sample
 | |
|     inbox = @inboxes.sample
 | |
| 
 | |
|     contact_inbox = find_or_create_contact_inbox(contact, inbox)
 | |
|     assignee = select_assignee(inbox)
 | |
|     team = select_team
 | |
|     priority = @priorities.sample
 | |
| 
 | |
|     contact_inbox.conversations.new(
 | |
|       account: @account,
 | |
|       inbox: inbox,
 | |
|       contact: contact,
 | |
|       assignee: assignee,
 | |
|       team: team,
 | |
|       priority: priority
 | |
|     )
 | |
|   end
 | |
| 
 | |
|   def find_or_create_contact_inbox(contact, inbox)
 | |
|     inbox.contact_inboxes.find_or_create_by!(
 | |
|       contact: contact,
 | |
|       source_id: SecureRandom.hex
 | |
|     )
 | |
|   end
 | |
| 
 | |
|   def select_assignee(inbox)
 | |
|     rand(10) < 8 ? inbox.members.sample : nil
 | |
|   end
 | |
| 
 | |
|   def select_team
 | |
|     rand(10) < 7 ? @teams.sample : nil
 | |
|   end
 | |
| 
 | |
|   def add_labels_to_conversation(conversation)
 | |
|     labels_to_add = @labels.sample(rand(5..20))
 | |
|     conversation.update_labels(labels_to_add.map(&:title))
 | |
|   end
 | |
| 
 | |
|   def create_messages_for_conversation(conversation)
 | |
|     message_creator = Seeders::Reports::MessageCreator.new(
 | |
|       account: @account,
 | |
|       agents: @agents,
 | |
|       conversation: conversation
 | |
|     )
 | |
|     message_creator.create_messages
 | |
|   end
 | |
| 
 | |
|   def trigger_conversation_resolved_event(conversation)
 | |
|     event_data = { conversation: conversation }
 | |
| 
 | |
|     ReportingEventListener.instance.conversation_resolved(
 | |
|       Events::Base.new('conversation_resolved', Time.current, event_data)
 | |
|     )
 | |
|   end
 | |
| end
 |