Module: Controllers::Attachable
- Extended by:
- ActiveSupport::Concern
- Included in:
- ActivitiesController, ArticleProceduresController, ArticleTechnicalsController, ArticleTrainingsController, CommunicationsController, Crm::ExportedCatalogItemPacketsController, Crm::PostCommentsController, Crm::PostsController, Crm::SmsMessagesController, OpportunitiesController, RmasController, SupportCasesController, TopicsController, VouchersController
- Defined in:
- app/concerns/controllers/attachable.rb
Constant Summary collapse
- PUBLICATIONS_PER_PAGE =
20
Instance Method Summary collapse
- #attach ⇒ Object
-
#publication_modal ⇒ Object
Render publication picker offcanvas on-demand into Turbo Frame This avoids nested forms by loading the offcanvas separately.
- #remove_attachment ⇒ Object
- #search_library ⇒ Object
Instance Method Details
#attach ⇒ Object
6 7 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 |
# File 'app/concerns/controllers/attachable.rb', line 6 def attach load_context # Set controller_path for route generation in components # This handles irregular controllers (e.g., article_trainings vs Article model) @controller_path = controller_name # Capture parent_form_id if provided (for new records - hidden fields need form attribute) @parent_form_id = params[:parent_form_id] # Determine the scenario: new record, existing record, or invalid record context_object_id = params[:context_object_id] || params[:id] is_new_record = context_object_id.blank? || context_object_id.to_i == 0 is_invalid_record = !is_new_record && @context_object.nil? is_existing_record = !is_new_record && @context_object.present? # Execute appropriate action based on scenario if is_new_record # New record: create uploads but don't attach them (will be submitted via hidden fields) perform_attach(nil) elsif is_invalid_record # Existing record ID provided but record not found - set flash error flash[:error] = 'Unable to attach: Communication not found. Please refresh the page.' @uploads = [] # Ensure @uploads is set for template elsif is_existing_record # Existing record: attach uploads to the record perform_attach(@context_object) end # Single response block for all scenarios # Flash messages are automatically handled by TurboStreamFlashable after_action respond_to do |format| format.turbo_stream { render 'attachable/attach' } end end |
#publication_modal ⇒ Object
Render publication picker offcanvas on-demand into Turbo Frame
This avoids nested forms by loading the offcanvas separately
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
# File 'app/concerns/controllers/attachable.rb', line 162 def publication_modal load_context @controller_path = controller_name context_object_id = params[:context_object_id] || @context_object&.id || 0 @offcanvas_id = "publication-picker-#{context_object_id}" render partial: 'attachable/publication_offcanvas', layout: false, locals: { offcanvas_id: @offcanvas_id, context_object: @context_object, context_class_name: params[:context_class], controller_path: @controller_path } end |
#remove_attachment ⇒ Object
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'app/concerns/controllers/attachable.rb', line 42 def @upload = Upload.find(params[:upload_id]) # Load context if provided (for existing records) if params[:context_object_id].present? load_context # For saved records: unlink the upload from the record, and only delete if safe # (uploaded files can be deleted, but literature/publications should only be unlinked) (@context_object) elsif @upload.ok_to_delete? && can?(:destroy, @upload) # For new/unsaved records (no context_object_id yet): # - Uploaded files: can be safely deleted (ok_to_delete? returns true) # - Literature/publications: should NOT be deleted (ok_to_delete? returns false) # The upload is removed from the DOM via Turbo Stream regardless @upload.destroy end respond_to do |format| format.turbo_stream { render 'attachable/remove_attachment' } format.html { redirect_back_or_to(root_path, notice: 'Attachment removed') } end end |
#search_library ⇒ Object
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 151 152 153 154 155 156 157 158 |
# File 'app/concerns/controllers/attachable.rb', line 67 def search_library load_context context_object_id = @context_object.try(:id).to_i is_append = params[:append].to_s == 'true' if params[:term].present? # Cursor-based pagination using item.id for keyset navigation # Eager load literature and primary_image to avoid N+1 queries publications_query = Item.publications.active .joins(:literature) .includes(:literature, :primary_image) .keywords_search(params[:term]) .order('items.id DESC') # Apply cursor filter if provided (keyset pagination) publications_query = publications_query.where('items.id < ?', params[:cursor].to_i) if params[:cursor].present? @publications = publications_query.limit(PUBLICATIONS_PER_PAGE + 1).to_a # Check if there are more results @has_more = @publications.size > PUBLICATIONS_PER_PAGE @publications = @publications.first(PUBLICATIONS_PER_PAGE) # Next cursor is the last item's ID @cursor = @publications.last&.id else @publications = [] @has_more = false @cursor = nil end # Get existing upload IDs to show "Already Attached" state existing_upload_ids = @context_object.try(:upload_ids) || [] respond_to do |format| format.turbo_stream do if is_append # Appending more results (infinite scroll) # Use multiple streams: append items to grid, replace trigger with new trigger render 'attachable/search_library_append', locals: { publications: @publications, existing_upload_ids: existing_upload_ids, context_object_id: context_object_id, context_class_name: params[:context_class], controller_path: controller_name, cursor: @cursor, has_more: @has_more, search_term: params[:term] } else # Fresh search - replace entire frame render turbo_stream: turbo_stream.update( "publication-picker-frame-#{context_object_id}", Crm::PublicationPickerComponent.new( offcanvas_id: "publication-picker-#{context_object_id}", context_object: @context_object, publications: @publications, existing_upload_ids: existing_upload_ids, body_only: true, search_term: params[:term], cursor: @cursor, has_more: @has_more ) ) end end format.html do if is_append # Turbo-frame lazy loading with turbo_stream response # Must set content_type so Turbo processes the stream actions render 'attachable/search_library_append', formats: [:turbo_stream], content_type: 'text/vnd.turbo-stream.html', locals: { publications: @publications, existing_upload_ids: existing_upload_ids, context_object_id: context_object_id, context_class_name: params[:context_class], controller_path: controller_name, cursor: @cursor, has_more: @has_more, search_term: params[:term] } elsif request.headers['Turbo-Frame'].present? render 'attachable/search_library', layout: false else render 'attachable/search_library' end end end end |