Class: Crm::ReviewsIoController
- Inherits:
-
CrmController
- Object
- ActionController::Base
- ApplicationController
- CrmController
- Crm::ReviewsIoController
- Defined in:
- app/controllers/crm/reviews_io_controller.rb
Overview
CRM controller for viewing Reviews.io data
This is a read-only interface - reviews cannot be created, edited, or deleted from CRM
All review management must be done through the Reviews.io dashboard
Constant Summary
Constants included from Controllers::ReferenceFindable
Controllers::ReferenceFindable::ID_EMBEDDED_PATTERNS
Constants included from Controllers::AnalyticsEvents
Controllers::AnalyticsEvents::MAX_QUEUED_EVENTS, Controllers::AnalyticsEvents::SESSION_KEY
Constants included from Controllers::ErrorRendering
Controllers::ErrorRendering::NON_CONTENT_PATH_PREFIXES
Instance Method Summary collapse
-
#generate_headline ⇒ Object
POST /reviews/:id/generate_headline Calls the LLM to suggest a headline; returns JSON — no page reload.
-
#import ⇒ void
Enqueues a background import of reviews from Reviews.io and redirects to the index.
-
#import_images ⇒ void
Imports the review's photos into the image library and redirects back with the result.
-
#index ⇒ void
Lists Reviews.io reviews with ransack filters, pagination, preloaded orders/items/images, and summary stats (counts and average ratings).
-
#show ⇒ void
Shows a single review with its images, tags, and the related order/customer when resolvable.
-
#toggle_hidden ⇒ void
Toggles the review's public visibility (
hiddenflag) and redirects to the review. -
#update_avatar ⇒ void
Sets (or clears) the review's avatar image, validating the image exists first.
-
#update_headline ⇒ Object
PATCH /reviews/:id/update_headline Persists (or clears) the custom_headline.
-
#update_tags ⇒ void
Replaces the review's tags with the submitted list and redirects to the review.
Methods inherited from CrmController
#access_denied, #context_id, #context_object, #crm_home_path, #current_ability, #default_url_options, #download_temp, #get_tempfile_path_for_download, #init_status_job_collector, #initialize_crm_lazy_chunks, #persist_enqueued_status_jobs, #record_not_found, #redirect_to_job_or_fallback, #render_edit_action, #set_context, #set_download_path, #stash_file_for_temp_download, #sync_admin_presence_cookie, #touch_employee_last_seen
Methods inherited from ApplicationController
#account_impersonated?, #add_to_flash, #after_sign_in_path_for, #bypass_forgery_protection?, #chat_enabled?, #cloudflare_cleared?, #default_catalog, #default_url_options, #enable_turbo_frames, #find_publication, #fix_invalid_accept_header, #init_js_utils, #is_globals_call?, #layout_by_resource, #locale_store, #redirect_to, #require_employee_for_crm, #set_base_host, #set_real_ip, #set_report_errors_for, #should_render_layout?, #skip_layout_for_turbo_frame?, #stamp_impersonation_context, #tab_frame_breakout_request?, #warmlyyours_canada_ip?, #warmlyyours_ip?, #y
Methods included from Controllers::ReturnPathHandling
#check_for_return_path, #redirect_to_return_path_or_default
Methods included from Controllers::AnalyticsEvents
#consume_queued_analytics_events, #registration_lead_type, #track_event
Methods included from Controllers::DeviceDetection
Methods included from Controllers::SubdomainDetection
#is_crm_request?, #is_www_request?, #json_request?
Methods included from Controllers::TurboSafeRedirect
Methods included from Controllers::TrackingDetection
#bot_request?, #gdpr_country?, #gdpr_country_data, #prevent_bots, #set_tracking_cookie, #track_visitor?
Methods included from Controllers::AcceleratedFileSending
#send_file_accelerated, #send_upload_accelerated
Methods included from Controllers::ErrorRendering
#excp_string, #mail_to_for_error_reporting, #render_400, #render_404, #render_406, #render_410, #render_500, #render_invalid_authenticity_token, #render_ip_spoof_error, #render_unpermitted_parameters, #safe_referer_or_fallback
Methods included from Controllers::TurnstileVerification
#load_turnstile_script_tag, #turnstile_lazy_widget, #turnstile_script_tag, #turnstile_widget, #validate_turnstile!
Methods included from Controllers::CloudflareCaching
edge_cached, #edge_cached_action?, #reset_cloudflare_cache, #set_cloudflare_cache, #skip_edge_cache!, #skip_session
Methods included from Controllers::Webpackable
#preload_webpack_fonts, #webpack_css_include, #webpack_css_url, #webpack_js_include, #wpd_is_running?
Methods included from Controllers::Localizable
#cloudflare_country_locale, #determine_request_locale, #geocoder_locale, #guest_user_locale_check, #locale_optional_www_auth_path?, #param_locale, #set_locale, #set_request_locale, #skip_localization?, #warmlyyours_ip_locale
Methods included from Controllers::Authenticable
#access_denied, #authenticate_account, #authenticate_account!, #authenticate_account_from_login_token!, #check_is_a_manager, #check_is_a_sales_manager, #check_is_an_admin, #check_is_an_employee, #check_party, #clear_mismatched_guest_user, #create_guest_user, #credentials?, #current_or_guest_user, #current_or_guest_user_id_read_only, #current_user, #devise_mapping, #fully_logged_in?, #generate_bot_id, #guest_user, #identifiable?, #init_current_user, #initialize_guest, #load_context_user, #logging_in, #resource, #resource_name, #restrict_access_for_non_employees, #scrubbed_request_path, #user_object, #warn_on_session_guest_id_leak
Methods included from UrlsHelper
#catalog_breadcrumb_links, #catalog_link, #catalog_link_for_product_line, #catalog_link_for_sku, #cms_link, #delocalized_path, #path_to_sales_product_sku, #path_to_sales_product_sku_for_product_line, #path_to_sales_product_sku_for_product_line_slug, #product_line_from_catalog_link, #protocol_neutral_url, #sanitize_external_url, #valid_external_url?
Instance Method Details
#generate_headline ⇒ Object
POST /reviews/:id/generate_headline
Calls the LLM to suggest a headline; returns JSON — no page reload.
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'app/controllers/crm/reviews_io_controller.rb', line 140 def generate_headline @review = ReviewsIo.find(params[:id]) if @review.comments.blank? return render json: { error: 'This review has no comment body to generate a headline from.' }, status: :unprocessable_content end headline = ReviewHeadlineGenerator.call(@review.comments) render json: { headline: headline } rescue RubyLLM::RateLimitError render json: { error: 'AI service is rate-limited. Please try again in a moment.' }, status: :too_many_requests rescue RubyLLM::Error => e render json: { error: "AI service error: #{e.}" }, status: :service_unavailable end |
#import ⇒ void
This method returns an undefined value.
Enqueues a background import of reviews from Reviews.io and redirects to the index.
86 87 88 89 |
# File 'app/controllers/crm/reviews_io_controller.rb', line 86 def import ReviewsIoImportWorker.perform_async redirect_to reviews_io_index_path, notice: 'Review import has been queued. It may take a few minutes to complete.' end |
#import_images ⇒ void
This method returns an undefined value.
Imports the review's photos into the image library and redirects back with the result.
127 128 129 130 131 132 133 134 135 136 |
# File 'app/controllers/crm/reviews_io_controller.rb', line 127 def import_images @review = ReviewsIo.find(params[:id]) result = ReviewsIoImageImporter.new(@review, force: params[:force].present?).call if result.success? redirect_to reviews_io_path(@review), notice: "Imported #{result.imported_count} photo(s) into the image library." else redirect_to reviews_io_path(@review), alert: "Import completed with errors: #{result.errors.join('; ')}" end end |
#index ⇒ void
This method returns an undefined value.
Lists Reviews.io reviews with ransack filters, pagination, preloaded orders/items/images,
and summary stats (counts and average ratings).
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'app/controllers/crm/reviews_io_controller.rb', line 10 def index params[:q] ||= {} # Wildcard search across multiple fields search_query = params[:search_query].to_s.strip.presence params[:q][:comments_or_title_or_sku_or_author_name_or_order_id_cont] = search_query if search_query @q = ReviewsIo.all.ransack(params[:q]) @q.sorts = 'review_date desc' if @q.sorts.blank? @reviews = @q.result # Filter by customer (through orders) if params.dig(:q, :by_customer_id).present? customer_order_ids = Order.where(customer_id: params[:q][:by_customer_id]).pluck(:id, :reference_number).flatten.map(&:to_s) @reviews = @reviews.where(order_id: customer_order_ids) if customer_order_ids.any? end @pagy, @reviews = pagy(@reviews, items: 25) # Preload orders by ID and by reference_number to avoid N+1 queries order_ids = @reviews.filter_map(&:order_id).uniq if order_ids.any? orders = Order.includes(:customer).where(id: order_ids).or(Order.includes(:customer).where(reference_number: order_ids)) @orders_by_id = orders.index_by { |o| o.id.to_s } @orders_by_reference = orders.index_by(&:reference_number) else @orders_by_id = {} @orders_by_reference = {} end # Preload items by SKU to avoid N+1 queries skus = @reviews.filter_map(&:sku).uniq @items_by_sku = skus.any? ? Item.where(sku: skus).index_by(&:sku) : {} # Preload which reviews have imported images review_ids = @reviews.map(&:id) @reviews_with_images = review_ids.any? ? ReviewsIoImage.where(reviews_io_id: review_ids).distinct.pluck(:reviews_io_id).to_set : Set.new # Fan out the 4 summary aggregates concurrently. Rails 7.1 async_count # returns an ActiveRecord::Promise; async_average likewise. Materialize # them all in one transform_values pass. promises = { product_review_count: ReviewsIo.active.product_reviews.async_count, store_review_count: ReviewsIo.active.store_reviews.async_count, product_avg_rating: ReviewsIo.active.product_reviews.async_average(:rating), store_avg_rating: ReviewsIo.active.store_reviews.async_average(:rating) } stats = promises.transform_values(&:value) @product_review_count = stats[:product_review_count] @store_review_count = stats[:store_review_count] @product_avg_rating = stats[:product_avg_rating]&.round(2) @store_avg_rating = stats[:store_avg_rating]&.round(2) # Tags for the filter dropdown @available_tags = ReviewsIo. end |
#show ⇒ void
This method returns an undefined value.
Shows a single review with its images, tags, and the related order/customer when resolvable.
70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'app/controllers/crm/reviews_io_controller.rb', line 70 def show @review = ReviewsIo.find(params[:id]) @review_images = @review.images.order(created_at: :desc) @available_tags = ReviewsIo. return if @review.order_id.blank? @order = Order.find_by(id: @review.order_id) || Order.find_by(reference_number: @review.order_id) customer = @order&.customer @customer_full_name = customer&.full_name.presence @customer_profile_name = customer&.profile&.friendly_name.presence end |
#toggle_hidden ⇒ void
This method returns an undefined value.
Toggles the review's public visibility (hidden flag) and redirects to the review.
93 94 95 96 97 98 |
# File 'app/controllers/crm/reviews_io_controller.rb', line 93 def toggle_hidden @review = ReviewsIo.find(params[:id]) @review.update!(hidden: !@review.hidden) status = @review.hidden? ? 'hidden from public' : 'visible to public' redirect_to reviews_io_path(@review), notice: "Review is now #{status}." end |
#update_avatar ⇒ void
This method returns an undefined value.
Sets (or clears) the review's avatar image, validating the image exists first.
111 112 113 114 115 116 117 118 119 120 121 122 123 |
# File 'app/controllers/crm/reviews_io_controller.rb', line 111 def update_avatar @review = ReviewsIo.find(params[:id]) image_id = review_avatar_params[:avatar_image_id].presence&.to_i if image_id && !Image.exists?(image_id) redirect_to reviews_io_path(@review), alert: "Image ##{image_id} not found in the library." return end @review.update!(avatar_image_id: image_id) notice = image_id ? "Avatar set to image ##{image_id}." : 'Avatar cleared.' redirect_to reviews_io_path(@review), notice: notice end |
#update_headline ⇒ Object
PATCH /reviews/:id/update_headline
Persists (or clears) the custom_headline. Returns JSON.
159 160 161 162 163 |
# File 'app/controllers/crm/reviews_io_controller.rb', line 159 def update_headline @review = ReviewsIo.find(params[:id]) @review.update!(custom_headline: params[:custom_headline].presence) render json: { custom_headline: @review.custom_headline } end |
#update_tags ⇒ void
This method returns an undefined value.
Replaces the review's tags with the submitted list and redirects to the review.
102 103 104 105 106 107 |
# File 'app/controllers/crm/reviews_io_controller.rb', line 102 def @review = ReviewsIo.find(params[:id]) = [:tags]&.reject(&:blank?) || [] @review.update!(tags: ) redirect_to reviews_io_path(@review), notice: 'Tags updated successfully.' end |