From 49acab85f50f1e9bed9cd8cc5ab1358a4d710dee Mon Sep 17 00:00:00 2001 From: Muhsin Date: Thu, 29 Jan 2026 19:04:17 +0400 Subject: [PATCH] feat: Add APIs --- app/builders/v2/reports/agent_summary_builder.rb | 7 ++++++- app/builders/v2/reports/base_summary_builder.rb | 5 +++++ app/builders/v2/reports/inbox_summary_builder.rb | 8 +++++++- app/builders/v2/reports/label_summary_builder.rb | 15 +++++++++++++++ app/builders/v2/reports/team_summary_builder.rb | 7 ++++++- app/helpers/api/v2/accounts/reports_helper.rb | 1 + .../dashboard/i18n/locale/en/report.json | 3 ++- .../reports/components/SummaryReports.vue | 7 +++++++ app/views/api/v2/accounts/reports/agents.csv.erb | 1 + app/views/api/v2/accounts/reports/inboxes.csv.erb | 1 + app/views/api/v2/accounts/reports/labels.csv.erb | 1 + app/views/api/v2/accounts/reports/teams.csv.erb | 1 + config/locales/en.yml | 4 ++++ 13 files changed, 57 insertions(+), 4 deletions(-) diff --git a/app/builders/v2/reports/agent_summary_builder.rb b/app/builders/v2/reports/agent_summary_builder.rb index 9b4541aaa..4fcb67935 100644 --- a/app/builders/v2/reports/agent_summary_builder.rb +++ b/app/builders/v2/reports/agent_summary_builder.rb @@ -8,13 +8,17 @@ class V2::Reports::AgentSummaryBuilder < V2::Reports::BaseSummaryBuilder private - attr_reader :conversations_count, :resolved_count, + attr_reader :conversations_count, :outgoing_messages_count, :resolved_count, :avg_resolution_time, :avg_first_response_time, :avg_reply_time def fetch_conversations_count account.conversations.where(created_at: range).group('assignee_id').count end + def fetch_outgoing_messages_count + account.messages.unscope(:order).where(created_at: range, message_type: :outgoing).joins(:conversation).group('conversations.assignee_id').count + end + def prepare_report account.account_users.map do |account_user| build_agent_stats(account_user) @@ -26,6 +30,7 @@ class V2::Reports::AgentSummaryBuilder < V2::Reports::BaseSummaryBuilder { id: user_id, conversations_count: conversations_count[user_id] || 0, + outgoing_messages_count: outgoing_messages_count[user_id] || 0, resolved_conversations_count: resolved_count[user_id] || 0, avg_resolution_time: avg_resolution_time[user_id], avg_first_response_time: avg_first_response_time[user_id], diff --git a/app/builders/v2/reports/base_summary_builder.rb b/app/builders/v2/reports/base_summary_builder.rb index d4a9e7c0b..0fd282296 100644 --- a/app/builders/v2/reports/base_summary_builder.rb +++ b/app/builders/v2/reports/base_summary_builder.rb @@ -10,6 +10,7 @@ class V2::Reports::BaseSummaryBuilder def load_data @conversations_count = fetch_conversations_count + @outgoing_messages_count = fetch_outgoing_messages_count load_reporting_events_data end @@ -42,6 +43,10 @@ class V2::Reports::BaseSummaryBuilder # Override this method end + def fetch_outgoing_messages_count + # Override this method + end + def group_by_key # Override this method end diff --git a/app/builders/v2/reports/inbox_summary_builder.rb b/app/builders/v2/reports/inbox_summary_builder.rb index 935afeb82..45f1dbd27 100644 --- a/app/builders/v2/reports/inbox_summary_builder.rb +++ b/app/builders/v2/reports/inbox_summary_builder.rb @@ -8,11 +8,12 @@ class V2::Reports::InboxSummaryBuilder < V2::Reports::BaseSummaryBuilder private - attr_reader :conversations_count, :resolved_count, + attr_reader :conversations_count, :outgoing_messages_count, :resolved_count, :avg_resolution_time, :avg_first_response_time, :avg_reply_time def load_data @conversations_count = fetch_conversations_count + @outgoing_messages_count = fetch_outgoing_messages_count load_reporting_events_data end @@ -20,6 +21,10 @@ class V2::Reports::InboxSummaryBuilder < V2::Reports::BaseSummaryBuilder account.conversations.where(created_at: range).group(group_by_key).count end + def fetch_outgoing_messages_count + account.messages.unscope(:order).where(created_at: range, message_type: :outgoing).group(:inbox_id).count + end + def prepare_report account.inboxes.map do |inbox| build_inbox_stats(inbox) @@ -30,6 +35,7 @@ class V2::Reports::InboxSummaryBuilder < V2::Reports::BaseSummaryBuilder { id: inbox.id, conversations_count: conversations_count[inbox.id] || 0, + outgoing_messages_count: outgoing_messages_count[inbox.id] || 0, resolved_conversations_count: resolved_count[inbox.id] || 0, avg_resolution_time: avg_resolution_time[inbox.id], avg_first_response_time: avg_first_response_time[inbox.id], diff --git a/app/builders/v2/reports/label_summary_builder.rb b/app/builders/v2/reports/label_summary_builder.rb index 8b7c21e8e..03f022556 100644 --- a/app/builders/v2/reports/label_summary_builder.rb +++ b/app/builders/v2/reports/label_summary_builder.rb @@ -28,6 +28,7 @@ class V2::Reports::LabelSummaryBuilder < V2::Reports::BaseSummaryBuilder { conversation_counts: fetch_conversation_counts(conversation_filter), + outgoing_messages_counts: fetch_outgoing_messages_count, resolved_counts: fetch_resolved_counts, resolution_metrics: fetch_metrics(conversation_filter, 'conversation_resolved', use_business_hours), first_response_metrics: fetch_metrics(conversation_filter, 'first_response', use_business_hours), @@ -40,6 +41,7 @@ class V2::Reports::LabelSummaryBuilder < V2::Reports::BaseSummaryBuilder id: label.id, name: label.title, conversations_count: report_data[:conversation_counts][label.title] || 0, + outgoing_messages_count: report_data[:outgoing_messages_counts][label.title] || 0, avg_resolution_time: report_data[:resolution_metrics][label.title] || 0, avg_first_response_time: report_data[:first_response_metrics][label.title] || 0, avg_reply_time: report_data[:reply_metrics][label.title] || 0, @@ -62,6 +64,19 @@ class V2::Reports::LabelSummaryBuilder < V2::Reports::BaseSummaryBuilder fetch_counts(conversation_filter) end + def fetch_outgoing_messages_count + Message.unscope(:order) + .joins(conversation: { taggings: :tag }) + .where( + account_id: account.id, + created_at: range, + message_type: :outgoing, + taggings: { taggable_type: 'Conversation', context: 'labels' } + ) + .group('tags.name') + .count + end + def fetch_resolved_counts # Count resolution events, not conversations currently in resolved status # Filter by reporting_event.created_at, not conversation.created_at diff --git a/app/builders/v2/reports/team_summary_builder.rb b/app/builders/v2/reports/team_summary_builder.rb index b98151bc6..af93d4ac2 100644 --- a/app/builders/v2/reports/team_summary_builder.rb +++ b/app/builders/v2/reports/team_summary_builder.rb @@ -3,13 +3,17 @@ class V2::Reports::TeamSummaryBuilder < V2::Reports::BaseSummaryBuilder private - attr_reader :conversations_count, :resolved_count, + attr_reader :conversations_count, :outgoing_messages_count, :resolved_count, :avg_resolution_time, :avg_first_response_time, :avg_reply_time def fetch_conversations_count account.conversations.where(created_at: range).group(:team_id).count end + def fetch_outgoing_messages_count + account.messages.unscope(:order).where(created_at: range, message_type: :outgoing).joins(:conversation).group('conversations.team_id').count + end + def reporting_events @reporting_events ||= account.reporting_events.where(created_at: range).joins(:conversation) end @@ -24,6 +28,7 @@ class V2::Reports::TeamSummaryBuilder < V2::Reports::BaseSummaryBuilder { id: team.id, conversations_count: conversations_count[team.id] || 0, + outgoing_messages_count: outgoing_messages_count[team.id] || 0, resolved_conversations_count: resolved_count[team.id] || 0, avg_resolution_time: avg_resolution_time[team.id], avg_first_response_time: avg_first_response_time[team.id], diff --git a/app/helpers/api/v2/accounts/reports_helper.rb b/app/helpers/api/v2/accounts/reports_helper.rb index 1f34d7e97..cee952a38 100644 --- a/app/helpers/api/v2/accounts/reports_helper.rb +++ b/app/helpers/api/v2/accounts/reports_helper.rb @@ -72,6 +72,7 @@ module Api::V2::Accounts::ReportsHelper def generate_readable_report_metrics(report) [ report[:conversations_count], + report[:outgoing_messages_count], Reports::TimeFormatPresenter.new(report[:avg_first_response_time]).format, Reports::TimeFormatPresenter.new(report[:avg_resolution_time]).format, Reports::TimeFormatPresenter.new(report[:avg_reply_time]).format, diff --git a/app/javascript/dashboard/i18n/locale/en/report.json b/app/javascript/dashboard/i18n/locale/en/report.json index 504caff0f..50738de51 100644 --- a/app/javascript/dashboard/i18n/locale/en/report.json +++ b/app/javascript/dashboard/i18n/locale/en/report.json @@ -622,6 +622,7 @@ "AVG_FIRST_RESPONSE_TIME": "Avg. First Response Time", "AVG_REPLY_TIME": "Avg. Customer Waiting Time", "RESOLUTION_COUNT": "Resolution Count", - "CONVERSATIONS": "No. of conversations" + "CONVERSATIONS": "No. of conversations", + "OUTGOING_MESSAGES": "No. of outgoing messages" } } diff --git a/app/javascript/dashboard/routes/dashboard/settings/reports/components/SummaryReports.vue b/app/javascript/dashboard/routes/dashboard/settings/reports/components/SummaryReports.vue index 9b966c8fe..33f7a9e8c 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/reports/components/SummaryReports.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/reports/components/SummaryReports.vue @@ -70,6 +70,11 @@ const columns = computed(() => [ width: 200, cell: defaulSpanRender, }), + columnHelper.accessor('outgoingMessagesCount', { + header: t('SUMMARY_REPORTS.OUTGOING_MESSAGES'), + width: 200, + cell: defaulSpanRender, + }), columnHelper.accessor('avgFirstResponseTime', { header: t('SUMMARY_REPORTS.AVG_FIRST_RESPONSE_TIME'), width: 200, @@ -101,6 +106,7 @@ const tableData = computed(() => const rowMetrics = getMetrics(row.id); const { conversationsCount, + outgoingMessagesCount, avgFirstResponseTime, avgResolutionTime, avgReplyTime, @@ -112,6 +118,7 @@ const tableData = computed(() => name: row.name ?? row.title, type: props.type, conversationsCount: renderCount(conversationsCount), + outgoingMessagesCount: renderCount(outgoingMessagesCount), avgFirstResponseTime: renderAvgTime(avgFirstResponseTime), avgReplyTime: renderAvgTime(avgReplyTime), avgResolutionTime: renderAvgTime(avgResolutionTime), diff --git a/app/views/api/v2/accounts/reports/agents.csv.erb b/app/views/api/v2/accounts/reports/agents.csv.erb index 3b5d765f1..a7364e1c6 100644 --- a/app/views/api/v2/accounts/reports/agents.csv.erb +++ b/app/views/api/v2/accounts/reports/agents.csv.erb @@ -3,6 +3,7 @@ <% headers = [ I18n.t('reports.agent_csv.agent_name'), I18n.t('reports.agent_csv.conversations_count'), + I18n.t('reports.agent_csv.outgoing_messages_count'), I18n.t('reports.agent_csv.avg_first_response_time'), I18n.t('reports.agent_csv.avg_resolution_time'), I18n.t('reports.agent_csv.avg_customer_waiting_time'), diff --git a/app/views/api/v2/accounts/reports/inboxes.csv.erb b/app/views/api/v2/accounts/reports/inboxes.csv.erb index df038d5fe..bb6688b24 100644 --- a/app/views/api/v2/accounts/reports/inboxes.csv.erb +++ b/app/views/api/v2/accounts/reports/inboxes.csv.erb @@ -4,6 +4,7 @@ I18n.t('reports.inbox_csv.inbox_name'), I18n.t('reports.inbox_csv.inbox_type'), I18n.t('reports.inbox_csv.conversations_count'), + I18n.t('reports.inbox_csv.outgoing_messages_count'), I18n.t('reports.inbox_csv.avg_first_response_time'), I18n.t('reports.inbox_csv.avg_resolution_time') ] diff --git a/app/views/api/v2/accounts/reports/labels.csv.erb b/app/views/api/v2/accounts/reports/labels.csv.erb index a9d420155..aa56039dd 100644 --- a/app/views/api/v2/accounts/reports/labels.csv.erb +++ b/app/views/api/v2/accounts/reports/labels.csv.erb @@ -3,6 +3,7 @@ <% headers = [ I18n.t('reports.label_csv.label_title'), I18n.t('reports.label_csv.conversations_count'), + I18n.t('reports.label_csv.outgoing_messages_count'), I18n.t('reports.label_csv.avg_first_response_time'), I18n.t('reports.label_csv.avg_resolution_time'), I18n.t('reports.label_csv.avg_reply_time'), diff --git a/app/views/api/v2/accounts/reports/teams.csv.erb b/app/views/api/v2/accounts/reports/teams.csv.erb index 6ba4a001f..c42c61742 100644 --- a/app/views/api/v2/accounts/reports/teams.csv.erb +++ b/app/views/api/v2/accounts/reports/teams.csv.erb @@ -3,6 +3,7 @@ <% headers = [ I18n.t('reports.team_csv.team_name'), I18n.t('reports.team_csv.conversations_count'), + I18n.t('reports.team_csv.outgoing_messages_count'), I18n.t('reports.team_csv.avg_first_response_time'), I18n.t('reports.team_csv.avg_resolution_time'), I18n.t('reports.team_csv.avg_customer_waiting_time'), diff --git a/config/locales/en.yml b/config/locales/en.yml index f8d5b119e..2894f176b 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -148,6 +148,7 @@ en: agent_csv: agent_name: Agent name conversations_count: Assigned conversations + outgoing_messages_count: Outgoing messages count avg_first_response_time: Avg first response time avg_resolution_time: Avg resolution time resolution_count: Resolution Count @@ -156,11 +157,13 @@ en: inbox_name: Inbox name inbox_type: Inbox type conversations_count: No. of conversations + outgoing_messages_count: Outgoing messages count avg_first_response_time: Avg first response time avg_resolution_time: Avg resolution time label_csv: label_title: Label conversations_count: No. of conversations + outgoing_messages_count: Outgoing messages count avg_first_response_time: Avg first response time avg_resolution_time: Avg resolution time avg_reply_time: Avg reply time @@ -168,6 +171,7 @@ en: team_csv: team_name: Team name conversations_count: Conversations count + outgoing_messages_count: Outgoing messages count avg_first_response_time: Avg first response time avg_resolution_time: Avg resolution time resolution_count: Resolution Count