Class: Crm::ShowcasesController

Inherits:
CrmController show all
Includes:
Controllers::Workflowable
Defined in:
app/controllers/crm/showcases_controller.rb

Overview

Controller: showcases.

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

Constants included from Www::SeoHelper

Www::SeoHelper::AWARDS, Www::SeoHelper::CA_ADDRESS, Www::SeoHelper::CA_BUSINESS_HOURS, Www::SeoHelper::CA_CONTACT_POINT, Www::SeoHelper::CA_CURRENCIES, Www::SeoHelper::CA_DESCRIPTION, Www::SeoHelper::CA_FOUNDING_DATE, Www::SeoHelper::CA_GLOBAL_LOCATION_NUMBER, Www::SeoHelper::CA_LEGAL_NAME, Www::SeoHelper::CA_LOCAL_BUSINESS, Www::SeoHelper::CA_ONLINE_STORE, Www::SeoHelper::CA_RETURN_POLICY, Www::SeoHelper::CA_SALES_DEPARTMENT, Www::SeoHelper::CA_SERVICE_AREA, Www::SeoHelper::CA_URL, Www::SeoHelper::CA_VAT_ID, Www::SeoHelper::CA_WAREHOUSE_DEPARTMENT, Www::SeoHelper::CA_WAREHOUSE_HOURS, Www::SeoHelper::COMPANY_EMAIL, Www::SeoHelper::COMPANY_LOGO, Www::SeoHelper::COMPANY_NAME, Www::SeoHelper::COMPANY_SLOGAN, Www::SeoHelper::EXPERTISE, Www::SeoHelper::FAX_NUMBER, Www::SeoHelper::GS1_COMPANY_PREFIX, Www::SeoHelper::ISO6523_CODE, Www::SeoHelper::PAYMENT_METHODS, Www::SeoHelper::PHONE_NUMBER, Www::SeoHelper::PRIMARY_NAICS, Www::SeoHelper::REFUND_TYPE, Www::SeoHelper::RETURN_FEES, Www::SeoHelper::RETURN_METHOD, Www::SeoHelper::RETURN_POLICY_CATEGORY, Www::SeoHelper::SECONDARY_NAICS, Www::SeoHelper::SOCIAL_PROFILES, Www::SeoHelper::US_ADDRESS, Www::SeoHelper::US_BUSINESS_HOURS, Www::SeoHelper::US_CONTACT_POINT, Www::SeoHelper::US_CURRENCIES, Www::SeoHelper::US_DESCRIPTION, Www::SeoHelper::US_FOUNDING_DATE, Www::SeoHelper::US_GLOBAL_LOCATION_NUMBER, Www::SeoHelper::US_IMAGE, Www::SeoHelper::US_LEGAL_NAME, Www::SeoHelper::US_LOCAL_BUSINESS, Www::SeoHelper::US_ONLINE_STORE, Www::SeoHelper::US_RETURN_POLICY, Www::SeoHelper::US_SALES_DEPARTMENT, Www::SeoHelper::US_SERVICE_AREA, Www::SeoHelper::US_TAX_ID, Www::SeoHelper::US_URL, Www::SeoHelper::US_WAREHOUSE_DEPARTMENT, Www::SeoHelper::US_WAREHOUSE_HOURS

Constants included from IconHelper

IconHelper::CUSTOM_ICON_MAP, IconHelper::CUSTOM_SVG_DIR, IconHelper::DEFAULT_FAMILY

Instance Method Summary collapse

Methods included from Controllers::Workflowable

#render_workflow_error_stream, #render_workflow_success_stream, #workflow_action, #workflow_action_complete

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

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?, #stamp_impersonation_context, #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, #track_event

Methods included from Controllers::DeviceDetection

#device_detector, #is_ie?

Methods included from Controllers::SubdomainDetection

#is_crm_request?, #is_www_request?, #json_request?

Methods included from Controllers::TurboSafeRedirect

#redirect_to

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 ApplicationHelper

#better_number_to_currency, #check_force_logout, #check_or_cross, #check_or_times, #embedded_tab_frame_id, #error_messages, #general_disclaimer_on_product_installation_and_local_codes, #gridjs_from_html_table, #gridjs_table, #is_wy_ip, #line_break, #parent_layout, #pass_or_fail, #render_error_messages_list, #render_video_card, #resolved_auth_form_turbo_frame, #return_path_or, #safe_css_color, #set_return_path_if_present, #set_section_if_present, #tab_frame_id, #to_underscore, #track_page?, #turbo_section_wrapper, #turbo_tabs_request?, #url_on_same_domain_as_request, #widget_index_daily_focus_index_path, #working_hours?, #yes_or_no, #yes_or_no_highlighted, #yes_or_no_with_check_or_cross, #youtube_video

Methods included from UppyUploaderHelper

#file_uploader, #image_uploader, #large_file_uploader_s3, #lead_sketch_uploader, #rma_image_uploader, #rma_image_uploader_s3, #uppy_uploader, #video_uploader

Methods included from Www::ImagesHelper

#image_asset_tag, #image_asset_url

Methods included from Www::SeoHelper

#add_page_schema, #add_webpage_schema, #canada?, #company_social_links, #ensure_context_json, #json_ld_script_tag, #local_business_schema, #online_store_id, #online_store_schema, #page_main_entity, #page_main_entity_json, #render_auto_collection_page_schema, #render_collection_page_schema, #render_local_business_schema, #render_online_store_schema, #render_page_schemas, #render_page_video_schemas, #render_webpage_schema, #render_webpage_schema_with_collections, #usa?

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?

Methods included from IconHelper

#account_nav_icon, #fa_icon, #star_rating_html

Instance Method Details

#check_post_conflictsObject

POST /showcases/check_post_conflicts
Params: post_ids[] (array of integers), exclude_id (custom_slug of current showcase, optional)
Returns: { warnings: [ { post_id, post_subject, showcase_slug, showcase_name, url } ] }



345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
# File 'app/controllers/crm/showcases_controller.rb', line 345

def check_post_conflicts
  ids = Array(params[:post_ids]).map(&:to_i).uniq.compact
  exclude_slug = params[:exclude_id].to_s.presence
  return render json: { warnings: [] } if ids.blank?

  scope = Showcase.published
  scope = scope.where.not(custom_slug: exclude_slug) if exclude_slug.present?

  # Find showcases that reference any of the given post_ids
  matches = scope
            .where('array_remove(post_ids, NULL) && ARRAY[?]::integer[]', ids)
            .select(:custom_slug, :name, :post_ids)

  warnings = []
  if matches.any?
    # Preload posts to map id -> subject for message clarity
    subjects = Post.where(id: ids).pluck(:id, :subject).to_h
    matches.each do |sc|
      overlapping = Array(sc.post_ids).compact & ids
      overlapping.each do |pid|
        warnings << {
          post_id: pid,
          post_subject: subjects[pid] || "Post ##{pid}",
          showcase_slug: sc.custom_slug,
          showcase_name: sc.name,
          url: Rails.application.routes.url_helpers.showcase_url(sc.custom_slug, host: request.host_with_port)
        }
      end
    end
  end

  render json: { warnings: warnings }
end

#createObject



161
162
163
164
165
166
167
168
169
# File 'app/controllers/crm/showcases_controller.rb', line 161

def create
  @showcase = Showcase.new(showcase_params)
  @showcase.state = 'draft'
  if @showcase.save
    redirect_to showcase_path(@showcase), notice: 'Showcase was successfully created.'
  else
    render :new, status: :unprocessable_content
  end
end

#destroyObject



179
180
181
182
# File 'app/controllers/crm/showcases_controller.rb', line 179

def destroy
  @showcase.destroy
  redirect_to showcases_url, notice: 'Showcase was successfully destroyed.'
end

#editObject



159
# File 'app/controllers/crm/showcases_controller.rb', line 159

def edit; end

#edit_imagesObject



188
189
190
# File 'app/controllers/crm/showcases_controller.rb', line 188

def edit_images
  # For parity, we could load assets; no room configuration joins
end

#flush_cacheObject



335
336
337
338
339
340
# File 'app/controllers/crm/showcases_controller.rb', line 335

def flush_cache
  authorize!(:update, Showcase)
  @showcase.touch
  @showcase.purge_edge_cache
  redirect_to showcase_path(@showcase), notice: 'Cache purge has been queued, this can take a couple minutes.'
end

#flush_edge_cacheObject



328
329
330
331
332
333
# File 'app/controllers/crm/showcases_controller.rb', line 328

def flush_edge_cache
  authorize!(:update, Showcase)
  Showcase.published.touch_all
  Showcase.flush_edge_cache
  redirect_to showcases_path, notice: 'Cache refreshed for all showcases, this can take a couple minutes.'
end

#get_data_from_roomObject

POST /showcases/get_data_from_room



307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
# File 'app/controllers/crm/showcases_controller.rb', line 307

def get_data_from_room
  rc = RoomConfiguration.find(params[:room_id])
  return head :not_found unless rc

  room_size = rc.insulation_surface || rc.installation_sqft
  render json: {
    room_type: rc.room_type.id,
    size: "#{room_size} sq.ft.",
    volts: rc.installation_plan_voltage_id,
    product_line_id: rc.heating_system_product_line_id,
    customer_id: rc.customer&.id,
    customer_name: rc.customer&.full_name,
    watts: rc.calculate_total_watts,
    amps: rc.calculate_total_amps,
    floor_type_id: rc.floor_type_id,
    sub_floor_type_id: rc.sub_floor_type_id,
    operating_cost: rc.calculate_operating_cost_rate_using_hep,
    quote_ids: rc.quote_ids
  }
end

#import_images_from_roomObject

POST /showcases/:id/import_images_from_room



284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
# File 'app/controllers/crm/showcases_controller.rb', line 284

def import_images_from_room
  redirect_to edit_images_showcase_path(@showcase), alert: 'No room configuration selected.' and return if params[:room_configuration_id].blank? && @showcase.room_configuration_id.blank?

  room_id = params[:room_configuration_id].presence || @showcase.room_configuration_id
  room_config = RoomConfiguration.find_by(id: room_id)
  redirect_to edit_images_showcase_path(@showcase), alert: 'Room configuration not found.' and return unless room_config

  # Import room layout, installation plan and photos similar to FloorPlanDisplay
  imported = 0
  if room_config.installation_plan_image.present?
    imported += 1 if create_image_link_from_upload(room_config.installation_plan_image, room_config, 'Installation Plan')
  end
  if room_config.room_layout_image.present?
    imported += 1 if create_image_link_from_upload(room_config.room_layout_image, room_config, 'Room Layout')
  end
  room_config.uploads.in_category('photo').each do |upload|
    imported += 1 if create_image_link_from_upload(upload, room_config, 'Photo')
  end

  redirect_to edit_images_showcase_path(@showcase), notice: (imported.positive? ? 'Images successfully imported from room configuration.' : 'No images were imported.')
end

#indexObject



8
9
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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'app/controllers/crm/showcases_controller.rb', line 8

def index
  @q = Showcase.all.ransack(params[:q])
  @q.sorts = 'created_at DESC' if @q.sorts.blank?
  @showcases = @q.result.includes(:customer, showcase_digital_assets: :digital_asset)

  # Fan out 16 aggregates concurrently via Rails 7.1 async_count.
  # Each Showcase.<scope>.async_count returns an ActiveRecord::Promise that
  # materializes when the value is read in transform_values(&:value).
  promises = {
    total:                              Showcase.async_count,
    published:                          Showcase.published.async_count,
    draft:                              Showcase.draft.async_count,
    archived:                           Showcase.archived.async_count,
    with_breadcrumbs:                   Showcase.where('COALESCE(array_length(breadcrumbs, 1), 0) > 0').async_count,
    with_room_types:                    Showcase.where('COALESCE(array_length(room_types, 1), 0) > 0').async_count,
    with_faqs:                          Showcase.joins(:faqs).distinct.async_count,
    # array_remove: don't count NULL placeholders as elements
    with_quotes:                        Showcase.where('COALESCE(array_length(array_remove(quote_ids, NULL), 1), 0) > 0').async_count,
    with_posts:                         Showcase.where('COALESCE(array_length(array_remove(post_ids, NULL), 1), 0) > 0').async_count,
    with_description:                   Showcase.where("COALESCE(NULLIF(BTRIM(description), ''), NULL) IS NOT NULL").async_count,
    with_seo_description:               Showcase.where("COALESCE(NULLIF(BTRIM(seo_description), ''), NULL) IS NOT NULL").async_count,
    with_seo_title:                     Showcase.where("COALESCE(NULLIF(BTRIM(seo_title), ''), NULL) IS NOT NULL").async_count,
    with_short_description:             Showcase.where("COALESCE(NULLIF(BTRIM(short_description), ''), NULL) IS NOT NULL").async_count,
    without_operating_cost_explanation: Showcase.where("COALESCE(NULLIF(BTRIM(operating_cost_explanation), ''), '') = ''").async_count,
    without_images:                     Showcase.where.missing(:showcase_digital_assets).async_count,
    without_display_options:            Showcase.where('COALESCE(jsonb_array_length(enabled_buttons), 0) = 0').async_count
  }
  counts = promises.transform_values(&:value)

  @total_showcases = counts[:total]
  @published_showcases = counts[:published]
  @draft_showcases = counts[:draft]
  @archived_showcases = counts[:archived]

  @published_percentage = percentage_of(@published_showcases, @total_showcases)
  @draft_percentage = percentage_of(@draft_showcases, @total_showcases)
  @archived_percentage = percentage_of(@archived_showcases, @total_showcases)

  @with_breadcrumbs = counts[:with_breadcrumbs]
  @without_breadcrumbs = @total_showcases - @with_breadcrumbs
  @breadcrumb_percentage = percentage_of(@with_breadcrumbs, @total_showcases)

  @with_room_types = counts[:with_room_types]
  @without_room_types = @total_showcases - @with_room_types
  @room_types_percentage = percentage_of(@with_room_types, @total_showcases)

  @with_faqs = counts[:with_faqs]
  @without_faqs = @total_showcases - @with_faqs
  @faqs_percentage = percentage_of(@with_faqs, @total_showcases)

  @with_quotes = counts[:with_quotes]
  @without_quotes = @total_showcases - @with_quotes
  @quotes_percentage = percentage_of(@with_quotes, @total_showcases)

  @with_posts = counts[:with_posts]
  @without_posts = @total_showcases - @with_posts
  @posts_percentage = percentage_of(@with_posts, @total_showcases)

  @with_description = counts[:with_description]
  @without_description = @total_showcases - @with_description
  @description_percentage = percentage_of(@with_description, @total_showcases)

  @with_seo_description = counts[:with_seo_description]
  @without_seo_description = @total_showcases - @with_seo_description
  @seo_description_percentage = percentage_of(@with_seo_description, @total_showcases)

  @with_seo_title = counts[:with_seo_title]
  @without_seo_title = @total_showcases - @with_seo_title
  @seo_title_percentage = percentage_of(@with_seo_title, @total_showcases)

  @with_short_description = counts[:with_short_description]
  @without_short_description = @total_showcases - @with_short_description
  @short_description_percentage = percentage_of(@with_short_description, @total_showcases)

  @without_operating_cost_explanation = counts[:without_operating_cost_explanation]
  @with_operating_cost_explanation = @total_showcases - @without_operating_cost_explanation
  @without_operating_cost_explanation_percentage = percentage_of(@without_operating_cost_explanation, @total_showcases)
  @with_operating_cost_explanation_percentage = percentage_of(@with_operating_cost_explanation, @total_showcases)

  @without_images = counts[:without_images]
  @with_images = @total_showcases - @without_images
  @without_images_percentage = percentage_of(@without_images, @total_showcases)

  @without_display_options = counts[:without_display_options]
  @with_display_options = @total_showcases - @without_display_options
  @without_display_options_percentage = percentage_of(@without_display_options, @total_showcases)

  # Extra filters - handle both '1' (with) and '0' (without) cases
  @showcases = apply_text_field_filter(@showcases, :with_description, 'description')
  @showcases = apply_text_field_filter(@showcases, :with_seo_description, 'seo_description')
  @showcases = apply_text_field_filter(@showcases, :with_seo_title, 'seo_title')
  @showcases = apply_text_field_filter(@showcases, :with_short_description, 'short_description')
  @showcases = apply_array_field_filter(@showcases, :with_quotes, 'quote_ids')
  @showcases = apply_array_field_filter(@showcases, :with_posts, 'post_ids')
  @showcases = apply_association_filter(@showcases, :with_faqs, :faqs)
  @showcases = apply_association_filter(@showcases, :without_images, :showcase_digital_assets, inverted: true)

  @showcases = @showcases.where("COALESCE(NULLIF(BTRIM(operating_cost_explanation), ''), '') = ''") if params[:without_operating_cost_explanation] == '1'
  @showcases = @showcases.where("COALESCE(NULLIF(BTRIM(operating_cost_explanation), ''), NULL) IS NOT NULL") if params[:with_operating_cost_explanation] == '1'
  @showcases = @showcases.where('COALESCE(jsonb_array_length(enabled_buttons), 0) = 0') if params[:without_display_options_checked] == '1'
  # Breadcrumbs quick filter
  if params.key?(:with_breadcrumbs)
    if params[:with_breadcrumbs].to_s == '1'
      @showcases = @showcases.where('COALESCE(array_length(breadcrumbs, 1), 0) > 0')
    elsif params[:with_breadcrumbs].to_s == '0'
      @showcases = @showcases.where('COALESCE(array_length(breadcrumbs, 1), 0) = 0')
    end
  end

  # Ensure scope-based filters apply even if Ransack skips custom scopes
  if params[:q].is_a?(ActionController::Parameters) || params[:q].is_a?(Hash)
    qparams = params[:q]
    if qparams.key?(:by_product_line_id)
      vals = Array(qparams[:by_product_line_id])
      @showcases = @showcases.by_product_line_id(vals) if vals.any?(&:present?)
    end
    if qparams.key?(:by_quote_id)
      vals = Array(qparams[:by_quote_id])
      @showcases = @showcases.by_quote_id(vals) if vals.any?(&:present?)
    end
    if qparams.key?(:by_sku)
      val = qparams[:by_sku].to_s
      @showcases = @showcases.by_sku(val) if val.present?
    end
    if qparams.key?(:tags_include)
      vals = Array(qparams[:tags_include])
      @showcases = @showcases.tags_include(vals) if vals.any?(&:present?)
    end
    if qparams.key?(:room_types_include)
      vals = Array(qparams[:room_types_include])
      @showcases = @showcases.room_types_include(vals) if vals.any?(&:present?)
    end
  end

  respond_to do |format|
    format.html do
      @pagy, @showcases = pagy(@showcases)
    end
    format.csv do
      send_data generate_csv(@showcases), filename: "showcases-#{Date.current}.csv"
    end
  end
end

POST /showcases/:id/link_images



236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
# File 'app/controllers/crm/showcases_controller.rb', line 236

def link_images
  image_ids = Array(params[:image_ids]).map(&:to_i).uniq

  redirect_to edit_images_showcase_path(@showcase), alert: 'No images selected.' and return if image_ids.blank?

  position_start = @showcase.showcase_digital_assets.maximum(:position).to_i

  created = 0
  image_ids.each_with_index do |image_id, idx|
    exists = @showcase.showcase_digital_assets.where(digital_asset_id: image_id).exists?
    next if exists

    @showcase.showcase_digital_assets.create!(
      digital_asset_id: image_id,
      position: position_start + idx + 1
    )
    created += 1
  end

  notice = created.positive? ? 'Images added to showcase.' : 'Selected images were already linked.'
  redirect_to edit_images_showcase_path(@showcase), notice: notice
end

POST /showcases/:id/link_videos



260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
# File 'app/controllers/crm/showcases_controller.rb', line 260

def link_videos
  video_ids = Array(params[:video_ids]).map(&:to_i).uniq

  redirect_to edit_images_showcase_path(@showcase), alert: 'No videos selected.' and return if video_ids.blank?

  position_start = @showcase.showcase_digital_assets.maximum(:position).to_i

  created = 0
  video_ids.each_with_index do |video_id, idx|
    exists = @showcase.showcase_digital_assets.where(digital_asset_id: video_id).exists?
    next if exists

    @showcase.showcase_digital_assets.create!(
      digital_asset_id: video_id,
      position: position_start + idx + 1
    )
    created += 1
  end

  notice = created.positive? ? "#{created} video(s) added to showcase." : 'Selected videos were already linked.'
  redirect_to edit_images_showcase_path(@showcase), notice: notice
end

#newObject



154
155
156
157
# File 'app/controllers/crm/showcases_controller.rb', line 154

def new
  @showcase = Showcase.new
  @showcase.enabled_buttons = %w[button_design_room button_floor_heating_quote button_customize_floor_plan]
end

#previewObject



184
185
186
# File 'app/controllers/crm/showcases_controller.rb', line 184

def preview
  render template: 'www/showcases/show', layout: 'www/cms_page'
end

#set_main_imageObject

POST /showcases/:id/set_main_image



211
212
213
214
215
216
217
218
219
220
221
# File 'app/controllers/crm/showcases_controller.rb', line 211

def set_main_image
  da_id = params[:digital_asset_id].to_i
  join = @showcase.showcase_digital_assets.find_by(digital_asset_id: da_id)
  if join
    @showcase.showcase_digital_assets.excluding(join).update_all(is_main: false)
    join.update!(is_main: true)
    redirect_to edit_images_showcase_path(@showcase), notice: 'Main image updated.'
  else
    redirect_to edit_images_showcase_path(@showcase), alert: 'Image not found.'
  end
end

#showObject



152
# File 'app/controllers/crm/showcases_controller.rb', line 152

def show; end

#sort_imagesObject



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'app/controllers/crm/showcases_controller.rb', line 192

def sort_images
  image_ids = params[:image_ids] || params[:digital_asset_ids]
  if image_ids.present?
    join_by_da = @showcase.showcase_digital_assets.where(digital_asset_id: image_ids).index_by(&:digital_asset_id)
    ShowcaseDigitalAsset.transaction do
      image_ids.each_with_index do |id, index|
        join_record = join_by_da[id.to_i]
        next unless join_record

        join_record.update!(position: index + 1)
      end
    end
    render json: { status: 'success' }
  else
    render json: { status: 'error', message: 'No images provided' }
  end
end

DELETE /showcases/:id/unlink_image



224
225
226
227
228
229
230
231
232
233
# File 'app/controllers/crm/showcases_controller.rb', line 224

def unlink_image
  da_id = params[:digital_asset_id].to_i
  join = @showcase.showcase_digital_assets.find_by(digital_asset_id: da_id)
  if join
    join.destroy!
    redirect_to edit_images_showcase_path(@showcase), notice: 'Image removed from showcase.'
  else
    redirect_to edit_images_showcase_path(@showcase), alert: 'Image link not found.'
  end
end

#updateObject



171
172
173
174
175
176
177
# File 'app/controllers/crm/showcases_controller.rb', line 171

def update
  if @showcase.update(showcase_params)
    redirect_to showcase_path(@showcase), notice: 'Showcase was successfully updated.'
  else
    render :edit, status: :unprocessable_content
  end
end