diff --git a/config/routes.rb b/config/routes.rb index 1481c14c7..296a61aa6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -77,6 +77,10 @@ Rails.application.routes.draw do resources :scenarios end resources :assistant_responses + resources :faq_suggestions, only: [:index, :show, :update] do + post :approve, on: :member + post :dismiss, on: :member + end resources :message_reports, only: [:create] resources :bulk_actions, only: [:create] resources :copilot_threads, only: [:index, :create] do diff --git a/enterprise/app/controllers/api/v1/accounts/captain/faq_suggestions_controller.rb b/enterprise/app/controllers/api/v1/accounts/captain/faq_suggestions_controller.rb new file mode 100644 index 000000000..871c0b223 --- /dev/null +++ b/enterprise/app/controllers/api/v1/accounts/captain/faq_suggestions_controller.rb @@ -0,0 +1,64 @@ +class Api::V1::Accounts::Captain::FaqSuggestionsController < Api::V1::Accounts::BaseController + before_action :current_account + before_action -> { check_authorization(Captain::Assistant) } + before_action :set_suggestions + before_action :set_suggestion, except: [:index] + + RESULTS_PER_PAGE = 25 + + def index + @current_page = permitted_params[:page] || 1 + filtered_query = apply_filters(@suggestions) + @suggestions_count = filtered_query.count + @suggestions = filtered_query.page(@current_page).per(RESULTS_PER_PAGE) + end + + def show; end + + def update + raise ActiveRecord::RecordNotFound unless @suggestion.open? + + @suggestion.update!(suggestion_params) + end + + def approve + attributes = params[:faq_suggestion].present? ? suggestion_params : {} + @response = Captain::FaqSuggestionApprovalService.new(@suggestion, attributes).perform + end + + def dismiss + raise ActiveRecord::RecordNotFound unless @suggestion.open? + + @suggestion.dismissed! + end + + private + + def apply_filters(base_query) + base_query = base_query.where(assistant_id: permitted_params[:assistant_id]) if permitted_params[:assistant_id].present? + base_query = base_query.where(status: permitted_params[:status]) if permitted_params[:status].present? + + if permitted_params[:search].present? + search_term = "%#{permitted_params[:search]}%" + base_query = base_query.where('question ILIKE :search OR answer ILIKE :search', search: search_term) + end + + base_query + end + + def set_suggestions + @suggestions = Current.account.captain_faq_suggestions.includes(:assistant).ordered + end + + def set_suggestion + @suggestion = @suggestions.find(permitted_params[:id]) + end + + def permitted_params + params.permit(:id, :assistant_id, :page, :status, :search) + end + + def suggestion_params + params.require(:faq_suggestion).permit(:question, :answer) + end +end diff --git a/enterprise/app/services/captain/faq_suggestion_approval_service.rb b/enterprise/app/services/captain/faq_suggestion_approval_service.rb new file mode 100644 index 000000000..ac0439251 --- /dev/null +++ b/enterprise/app/services/captain/faq_suggestion_approval_service.rb @@ -0,0 +1,27 @@ +class Captain::FaqSuggestionApprovalService + def initialize(suggestion, attributes = {}) + @suggestion = suggestion + @attributes = attributes + end + + def perform + suggestion.with_lock do + raise ActiveRecord::RecordNotFound unless suggestion.open? + + suggestion.update!(attributes) if attributes.present? + + response = suggestion.assistant.responses.create!( + question: suggestion.question, + answer: suggestion.answer, + status: :approved + ) + suggestion.observations.delete_all + suggestion.approved! + response + end + end + + private + + attr_reader :suggestion, :attributes +end diff --git a/enterprise/app/views/api/v1/accounts/captain/faq_suggestions/approve.json.jbuilder b/enterprise/app/views/api/v1/accounts/captain/faq_suggestions/approve.json.jbuilder new file mode 100644 index 000000000..c4c7d2508 --- /dev/null +++ b/enterprise/app/views/api/v1/accounts/captain/faq_suggestions/approve.json.jbuilder @@ -0,0 +1 @@ +json.partial! 'api/v1/models/captain/assistant_response', formats: [:json], resource: @response diff --git a/enterprise/app/views/api/v1/accounts/captain/faq_suggestions/dismiss.json.jbuilder b/enterprise/app/views/api/v1/accounts/captain/faq_suggestions/dismiss.json.jbuilder new file mode 100644 index 000000000..ae04aed8a --- /dev/null +++ b/enterprise/app/views/api/v1/accounts/captain/faq_suggestions/dismiss.json.jbuilder @@ -0,0 +1 @@ +json.partial! 'api/v1/models/captain/faq_suggestion', formats: [:json], resource: @suggestion diff --git a/enterprise/app/views/api/v1/accounts/captain/faq_suggestions/index.json.jbuilder b/enterprise/app/views/api/v1/accounts/captain/faq_suggestions/index.json.jbuilder new file mode 100644 index 000000000..1688806d7 --- /dev/null +++ b/enterprise/app/views/api/v1/accounts/captain/faq_suggestions/index.json.jbuilder @@ -0,0 +1,10 @@ +json.payload do + json.array! @suggestions do |suggestion| + json.partial! 'api/v1/models/captain/faq_suggestion', formats: [:json], resource: suggestion + end +end + +json.meta do + json.total_count @suggestions_count + json.page @current_page +end diff --git a/enterprise/app/views/api/v1/accounts/captain/faq_suggestions/show.json.jbuilder b/enterprise/app/views/api/v1/accounts/captain/faq_suggestions/show.json.jbuilder new file mode 100644 index 000000000..012238d0c --- /dev/null +++ b/enterprise/app/views/api/v1/accounts/captain/faq_suggestions/show.json.jbuilder @@ -0,0 +1,6 @@ +json.partial! 'api/v1/models/captain/faq_suggestion', formats: [:json], resource: @suggestion +json.observations do + json.array! @suggestion.observations.includes(:conversation) do |observation| + json.partial! 'api/v1/models/captain/faq_observation', formats: [:json], resource: observation + end +end diff --git a/enterprise/app/views/api/v1/accounts/captain/faq_suggestions/update.json.jbuilder b/enterprise/app/views/api/v1/accounts/captain/faq_suggestions/update.json.jbuilder new file mode 100644 index 000000000..ae04aed8a --- /dev/null +++ b/enterprise/app/views/api/v1/accounts/captain/faq_suggestions/update.json.jbuilder @@ -0,0 +1 @@ +json.partial! 'api/v1/models/captain/faq_suggestion', formats: [:json], resource: @suggestion diff --git a/enterprise/app/views/api/v1/models/captain/_faq_observation.json.jbuilder b/enterprise/app/views/api/v1/models/captain/_faq_observation.json.jbuilder new file mode 100644 index 000000000..4b1686081 --- /dev/null +++ b/enterprise/app/views/api/v1/models/captain/_faq_observation.json.jbuilder @@ -0,0 +1,9 @@ +json.id resource.id +json.generated_question resource.generated_question +json.generated_answer resource.generated_answer +json.status resource.status +json.created_at resource.created_at.to_i +json.conversation do + json.id resource.conversation.id + json.display_id resource.conversation.display_id +end diff --git a/enterprise/app/views/api/v1/models/captain/_faq_suggestion.json.jbuilder b/enterprise/app/views/api/v1/models/captain/_faq_suggestion.json.jbuilder new file mode 100644 index 000000000..954f4216e --- /dev/null +++ b/enterprise/app/views/api/v1/models/captain/_faq_suggestion.json.jbuilder @@ -0,0 +1,11 @@ +json.id resource.id +json.account_id resource.account_id +json.question resource.question +json.answer resource.answer +json.source_count resource.source_count +json.status resource.status +json.created_at resource.created_at.to_i +json.updated_at resource.updated_at.to_i +json.assistant do + json.partial! 'api/v1/models/captain/assistant', formats: [:json], resource: resource.assistant +end