feat(reports): filtro por inbox no Relatório do Bot
Hoje as métricas e séries do BotReports agregam toda a conta — não dá pra ver "a Jasmine da PrimeAL está errando mais que a do Qnn01". Cada unidade tem prompt próprio, então um sintoma localizado precisa de medição localizada. Backend: - Inbox#has_many :reporting_events (relação inversa que faltava) - BotMetricsBuilder aceita inbox_id e filtra bot_conversations + base_reporting_events - bot_metrics endpoint passa inbox_id pelos params permitidos - count_report_builder já suporta scope=inbox; agora funciona pra bot_resolutions_count e bot_handoffs_count graças à relação acima Frontend: - BotReports.vue: ReportFilters com filter-type='inboxes' e dropdown ativo - Quando uma inbox é escolhida, requestPayload inclui inboxId/type/id e os fetches (BotMetrics + ReportContainer) passam o filtro - API client getBotMetrics aceita inboxId; getBotSummary aceita type+id - Sem inbox selecionada: comportamento antigo (agregação da conta) Bonus na rake task de retroativo: - rebuild_bot_resolved.rake: Message.unscope(:order) pra evitar conflito PG::InvalidColumnReference (DISTINCT + ORDER BY default scope) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
7cd2ea1258
commit
e9b8b6e587
@ -18,8 +18,15 @@ class V2::Reports::BotMetricsBuilder
|
||||
|
||||
private
|
||||
|
||||
def filter_inbox_id
|
||||
@filter_inbox_id ||= params[:inbox_id].presence&.to_i
|
||||
end
|
||||
|
||||
def bot_activated_inbox_ids
|
||||
@bot_activated_inbox_ids ||= account.inboxes.filter(&:active_bot?).map(&:id)
|
||||
@bot_activated_inbox_ids ||= begin
|
||||
ids = account.inboxes.filter(&:active_bot?).map(&:id)
|
||||
filter_inbox_id ? ids & [filter_inbox_id] : ids
|
||||
end
|
||||
end
|
||||
|
||||
def bot_conversations
|
||||
@ -30,14 +37,18 @@ class V2::Reports::BotMetricsBuilder
|
||||
@bot_messages ||= account.messages.outgoing.where(conversation_id: bot_conversations.ids).where(created_at: range)
|
||||
end
|
||||
|
||||
def base_reporting_events
|
||||
scope = account.reporting_events.where(account_id: account.id, created_at: range)
|
||||
scope = scope.where(inbox_id: filter_inbox_id) if filter_inbox_id
|
||||
scope
|
||||
end
|
||||
|
||||
def bot_resolutions_count
|
||||
account.reporting_events.joins(:conversation).select(:conversation_id).where(account_id: account.id, name: :conversation_bot_resolved,
|
||||
created_at: range).distinct.count
|
||||
base_reporting_events.joins(:conversation).select(:conversation_id).where(name: :conversation_bot_resolved).distinct.count
|
||||
end
|
||||
|
||||
def bot_handoffs_count
|
||||
account.reporting_events.joins(:conversation).select(:conversation_id).where(account_id: account.id, name: :conversation_bot_handoff,
|
||||
created_at: range).distinct.count
|
||||
base_reporting_events.joins(:conversation).select(:conversation_id).where(name: :conversation_bot_handoff).distinct.count
|
||||
end
|
||||
|
||||
def bot_resolution_rate
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
# rubocop:disable Metrics/ClassLength
|
||||
class Api::V2::Accounts::ReportsController < Api::V1::Accounts::BaseController
|
||||
include Api::V2::Accounts::ReportsHelper
|
||||
include Api::V2::Accounts::HeatmapHelper
|
||||
@ -58,7 +59,7 @@ class Api::V2::Accounts::ReportsController < Api::V1::Accounts::BaseController
|
||||
end
|
||||
|
||||
def bot_metrics
|
||||
bot_metrics = V2::Reports::BotMetricsBuilder.new(Current.account, params).metrics
|
||||
bot_metrics = V2::Reports::BotMetricsBuilder.new(Current.account, bot_metrics_params).metrics
|
||||
render json: bot_metrics
|
||||
end
|
||||
|
||||
@ -196,6 +197,14 @@ class Api::V2::Accounts::ReportsController < Api::V1::Accounts::BaseController
|
||||
}
|
||||
end
|
||||
|
||||
def bot_metrics_params
|
||||
{
|
||||
inbox_id: params[:inbox_id],
|
||||
since: params[:since],
|
||||
until: params[:until]
|
||||
}
|
||||
end
|
||||
|
||||
def inbox_leads_summary_params
|
||||
{
|
||||
inbox_id: params[:inbox_id],
|
||||
@ -205,3 +214,4 @@ class Api::V2::Accounts::ReportsController < Api::V1::Accounts::BaseController
|
||||
}
|
||||
end
|
||||
end
|
||||
# rubocop:enable Metrics/ClassLength
|
||||
|
||||
@ -91,18 +91,19 @@ class ReportsAPI extends ApiClient {
|
||||
});
|
||||
}
|
||||
|
||||
getBotMetrics({ from, to } = {}) {
|
||||
getBotMetrics({ from, to, inboxId } = {}) {
|
||||
return axios.get(`${this.url}/bot_metrics`, {
|
||||
params: { since: from, until: to },
|
||||
params: { since: from, until: to, inbox_id: inboxId },
|
||||
});
|
||||
}
|
||||
|
||||
getBotSummary({ from, to, groupBy, businessHours } = {}) {
|
||||
getBotSummary({ from, to, groupBy, businessHours, type, id } = {}) {
|
||||
return axios.get(`${this.url}/bot_summary`, {
|
||||
params: {
|
||||
since: from,
|
||||
until: to,
|
||||
type: 'account',
|
||||
type: type || 'account',
|
||||
id,
|
||||
group_by: groupBy,
|
||||
business_hours: businessHours,
|
||||
},
|
||||
|
||||
@ -20,6 +20,7 @@ export default {
|
||||
from: 0,
|
||||
to: 0,
|
||||
groupBy: GROUP_BY_FILTER[1],
|
||||
inboxId: null,
|
||||
reportKeys: {
|
||||
BOT_RESOLUTION_COUNT: 'bot_resolutions_count',
|
||||
BOT_HANDOFF_COUNT: 'bot_handoffs_count',
|
||||
@ -32,6 +33,7 @@ export default {
|
||||
return {
|
||||
from: this.from,
|
||||
to: this.to,
|
||||
inboxId: this.inboxId,
|
||||
};
|
||||
},
|
||||
},
|
||||
@ -60,24 +62,35 @@ export default {
|
||||
});
|
||||
},
|
||||
getRequestPayload() {
|
||||
const { from, to, groupBy, businessHours } = this;
|
||||
|
||||
return {
|
||||
const { from, to, groupBy, businessHours, inboxId } = this;
|
||||
const payload = {
|
||||
from,
|
||||
to,
|
||||
groupBy: groupBy?.period,
|
||||
businessHours,
|
||||
};
|
||||
if (inboxId) {
|
||||
payload.type = 'inbox';
|
||||
payload.id = inboxId;
|
||||
}
|
||||
return payload;
|
||||
},
|
||||
onFilterChange({ from, to, groupBy, businessHours }) {
|
||||
onFilterChange({ from, to, groupBy, businessHours, inboxes }) {
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
this.groupBy = groupBy;
|
||||
this.businessHours = businessHours;
|
||||
this.inboxId = inboxes?.id || null;
|
||||
this.fetchAllData();
|
||||
|
||||
useTrack(REPORTS_EVENTS.FILTER_REPORT, {
|
||||
filterValue: { from, to, groupBy, businessHours },
|
||||
filterValue: {
|
||||
from,
|
||||
to,
|
||||
groupBy,
|
||||
businessHours,
|
||||
inboxId: this.inboxId,
|
||||
},
|
||||
reportType: 'bots',
|
||||
});
|
||||
},
|
||||
@ -89,7 +102,7 @@ export default {
|
||||
<ReportHeader :header-title="$t('BOT_REPORTS.HEADER')" />
|
||||
<div class="flex flex-col gap-4">
|
||||
<ReportFilters
|
||||
:show-entity-filter="false"
|
||||
filter-type="inboxes"
|
||||
show-group-by
|
||||
:show-business-hours="false"
|
||||
@filter-change="onFilterChange"
|
||||
|
||||
@ -180,6 +180,8 @@ export const actions = {
|
||||
to: reportObj.to,
|
||||
groupBy: reportObj.groupBy,
|
||||
businessHours: reportObj.businessHours,
|
||||
type: reportObj.type,
|
||||
id: reportObj.id,
|
||||
})
|
||||
.then(botSummary => {
|
||||
commit(types.default.SET_BOT_SUMMARY, botSummary.data);
|
||||
|
||||
@ -77,6 +77,7 @@ class Inbox < ApplicationRecord
|
||||
has_many :conversations, dependent: :destroy_async
|
||||
has_many :messages, dependent: :destroy_async
|
||||
has_many :scheduled_messages, dependent: :destroy_async
|
||||
has_many :reporting_events, dependent: :nullify
|
||||
|
||||
has_one :inbox_assignment_policy, dependent: :destroy
|
||||
has_one :assignment_policy, through: :inbox_assignment_policy
|
||||
|
||||
@ -38,6 +38,7 @@ namespace :reports do
|
||||
base_scope = base_scope.where(account_id: account_id) if account_id
|
||||
|
||||
affected_conversation_ids = Message
|
||||
.unscope(:order)
|
||||
.where(message_type: :outgoing, sender_id: nil)
|
||||
.distinct
|
||||
.pluck(:conversation_id)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user