cm-admin 2.1.5 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 60f28f773c4032f2bed61d3b0524ab635b13face45767f444fbc841e490bc597
4
- data.tar.gz: 42433de9bf7714a37d0fe14bb99a6944bced6886d06cef2083c540e9acf33e93
3
+ metadata.gz: ab22f4bef2b77930d17a0910dbc5ee20f0773f9f025d7e7ba7fde8a55be7e5f8
4
+ data.tar.gz: ba334104c29113fbec5440dbe31b13b8fb8513efd6a8be7308928815141256a0
5
5
  SHA512:
6
- metadata.gz: d224d531688d916b36f07bce3d1e3b0a58939dbf5d847158d1bd051fd4c36750cfeae50986244c3be6e92690e427881b3fa5571b52cd1fe42b6e6edd029f8ac2
7
- data.tar.gz: be325872f019d39cc6b86cd06a0bc933631548e6da7f292b40a9f1bf2fc5b6149644b43305db3f803a4788239b1a8e25e5e53f848ba013afcc2d0cce6ef842b6
6
+ metadata.gz: 67c7bf36f684d04d976fbad85a58f5910a84762f2e750918b3ab2eef6b7c9b4471bb9ff6f8b1594bb8dc7f3bf9910cf6b4560d9e80ad8ae24d6a8b56321901ae
7
+ data.tar.gz: 47b9d696e72e73ef748ffc51301891dc2fa3667f3f201ec61643167d13b286d0dc289ec7ddd4a867700c93a1b23bdbb5395b747d82e7fc71e00c39fc42e77e44
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cm-admin (2.1.5)
4
+ cm-admin (2.2.0)
5
5
  caxlsx_rails
6
6
  cocoon (~> 1.2.15)
7
7
  csv-importer (~> 0.8.2)
@@ -19,6 +19,9 @@ var CmFilter = {
19
19
  // Main method which will structure the existing filter values with the newly
20
20
  // applied filter. Send and receive the value from the backend.
21
21
  var getFilteredData = function(filterType, filterValue, filterColumn=null) {
22
+ let sortColumn = $('[data-behaviour="sort-column"]').val();
23
+ let sortDirection = $('[data-behaviour="sort-direction"]:checked').val();
24
+
22
25
  var url = window.location.pathname
23
26
 
24
27
  // Based on the value changed for recent filter generate the filterParams hash
@@ -27,15 +30,16 @@ var getFilteredData = function(filterType, filterValue, filterColumn=null) {
27
30
  filterParams[filterType] = {};
28
31
  filterParams[filterType][filterColumn] = filterValue
29
32
  } else {
30
- filterParams[filterType] = filterValue
33
+ if (filterType) filterParams[filterType] = filterValue
31
34
  }
32
35
 
33
- // TODO add sort params in the queryString
34
36
  // page params is reinitialized to 1 when any new filter value is applied so
35
37
  // that it won't throw the error when the user doesn't have sufficent data
36
38
  // to display on the table.
37
39
  var queryString = {
38
40
  filters: filterParams,
41
+ sort_column: sortColumn,
42
+ sort_direction: sortDirection,
39
43
  page: 1
40
44
  };
41
45
 
@@ -346,4 +350,27 @@ $(document).on('click', '[data-behaviour="selected-chip"]', function(e) {
346
350
  $(selectElement).parent().siblings(':first').addClass('search-area').removeClass('search-with-chips')
347
351
  $(selectElement).parent().siblings(':last').removeClass('active')
348
352
  }
349
- })
353
+ })
354
+
355
+ $(document).on("change", '[data-behaviour="sort-column"], [data-behaviour="sort-direction"]', function (e) {
356
+ getFilteredData(null, null, null);
357
+ });
358
+
359
+ $(document).on("click", '[data-behaviour="reset-sort"]', function (e) {
360
+ let url = window.location.pathname;
361
+ let queryString = getParamsAsObject(window.location.search);
362
+ delete queryString["sort_column"];
363
+ delete queryString["sort_direction"];
364
+ let queryParam = jQuery.param(queryString);
365
+ $.ajax({
366
+ type: 'POST',
367
+ url: url + "/reset_sort_columns",
368
+ success: function(data) {
369
+ window.history.pushState("", "", url + "?" + queryParam);
370
+ window.location.reload();
371
+ },
372
+ error: function(jqxhr, textStatus, errorThrown) {
373
+ console.log(errorThrown, textStatus);
374
+ }
375
+ });
376
+ });
@@ -23,7 +23,6 @@ Select2();
23
23
 
24
24
  // import '@nathanvda/cocoon'
25
25
  import "daterangepicker";
26
- import "@fortawesome/fontawesome-free";
27
26
  import jqueryJgrowl from "jgrowl";
28
27
 
29
28
  document.addEventListener("turbo:load", function () {
@@ -199,3 +199,23 @@
199
199
  height: 31px;
200
200
  }
201
201
  }
202
+
203
+ .sort-button {
204
+ @extend .btn, .btn-outline-dark, .btn-sm, .d-flex;
205
+ gap: 4px;
206
+ }
207
+
208
+ .sort-menu {
209
+ padding: 0;
210
+ &__top {
211
+ @extend .p-3, .d-flex, .flex-column, .gap-3;
212
+ }
213
+ &__bottom {
214
+ @extend .d-flex, .px-3, .py-2, .bg-light, .rounded-2;
215
+ justify-content: center;
216
+ align-items: center;
217
+ button {
218
+ width: 100%;
219
+ }
220
+ }
221
+ }
@@ -21,6 +21,5 @@
21
21
  @import "./base/alertbanner";
22
22
  @import "./components/index";
23
23
  @import "./dependency/flatpickr.min";
24
- @import "./dependency/fontawesome.all";
25
24
  @import "./dependency/jquery-jgrowl.min";
26
25
  @import "./scaffold";
@@ -5,19 +5,21 @@ module CmAdmin
5
5
 
6
6
  helper CmAdmin::ViewHelpers
7
7
 
8
+ skip_before_action :verify_authenticity_token, only: :reset_sort_columns
9
+
8
10
  def cm_index(params)
9
11
  @current_action = CmAdmin::Models::Action.find_by(@model, name: 'index')
10
12
  # Based on the params the filter and pagination object to be set
11
13
  authorize @ar_object, policy_class: "CmAdmin::#{controller_name.classify}Policy".constantize if defined? "CmAdmin::#{controller_name.classify}Policy".constantize
12
14
  records = "CmAdmin::#{@model.name}Policy::IndexScope".constantize.new(Current.user, @model.name.constantize).resolve
13
15
  records = apply_scopes(records)
14
- if (['table', 'card'].include?(params[:view_type]) || [:table, :card].include?(@current_action.view_type))
15
- @ar_object = filter_by(params, records, filter_params: @model.filter_params(params))
16
- elsif (request.xhr? && params[:view_type] == 'kanban') || @current_action.view_type == :kanban
17
- @ar_object = kanban_filter_by(params, records, @model.filter_params(params))
18
- else
19
- @ar_object = filter_by(params, records, filter_params: @model.filter_params(params))
20
- end
16
+ @ar_object = if %w[table card].include?(params[:view_type]) || %i[table card].include?(@current_action.view_type)
17
+ filter_by(params, records, filter_params: @model.filter_params(params))
18
+ elsif (request.xhr? && params[:view_type] == 'kanban') || @current_action.view_type == :kanban
19
+ kanban_filter_by(params, records, @model.filter_params(params))
20
+ else
21
+ filter_by(params, records, filter_params: @model.filter_params(params))
22
+ end
21
23
  respond_to do |format|
22
24
  if request.xhr? && (params[:view_type] == 'kanban' || @current_action.view_type == :kanban)
23
25
  format.json { render json: @ar_object }
@@ -45,7 +47,7 @@ module CmAdmin
45
47
  end
46
48
  end
47
49
 
48
- def cm_new(params)
50
+ def cm_new(_params)
49
51
  @current_action = CmAdmin::Models::Action.find_by(@model, name: 'new')
50
52
  @ar_object = @model.ar_model.new
51
53
  resource_identifier
@@ -91,19 +93,17 @@ module CmAdmin
91
93
  end
92
94
 
93
95
  def import
94
- @model = Model.find_by({name: controller_name.classify})
95
- allowed_params = params.permit(file_import: [:associated_model_name, :import_file]).to_h
96
+ @model = Model.find_by({ name: controller_name.classify })
97
+ allowed_params = params.permit(file_import: %i[associated_model_name import_file]).to_h
96
98
  file_import = ::FileImport.new(allowed_params[:file_import])
97
99
  file_import.added_by = Current.user
98
100
  respond_to do |format|
99
- if file_import.save!
100
- format.html { redirect_back fallback_location: cm_admin.send("#{@model.name.underscore}_index_path"), notice: "Your import is successfully queued." }
101
- end
101
+ format.html { redirect_back fallback_location: cm_admin.send("#{@model.name.underscore}_index_path"), notice: 'Your import is successfully queued.' } if file_import.save!
102
102
  end
103
103
  end
104
104
 
105
105
  def import_form
106
- @model = Model.find_by({name: controller_name.classify})
106
+ @model = Model.find_by({ name: controller_name.classify })
107
107
  respond_to do |format|
108
108
  format.html { render '/cm_admin/main/import_form' }
109
109
  end
@@ -116,9 +116,9 @@ module CmAdmin
116
116
  if @bulk_action_processor.invalid_records.empty?
117
117
  format.html { redirect_to request.referrer, notice: "#{@action.name.humanize} is successful" }
118
118
  else
119
- error_messages = @bulk_action_processor.invalid_records.map { |invalid_record|
119
+ error_messages = @bulk_action_processor.invalid_records.map do |invalid_record|
120
120
  "<li>#{invalid_record.error_message}</li>"
121
- }.join
121
+ end.join
122
122
  format.html { redirect_to request.referrer, alert: "<b>#{@action.name.humanize} is unsuccessful</b><br /><ul>#{error_messages}</ul>" }
123
123
  end
124
124
  end
@@ -135,7 +135,7 @@ module CmAdmin
135
135
  def cm_custom_method(params)
136
136
  records = "CmAdmin::#{@model.name}Policy::#{@action.name.classify}Scope".constantize.new(Current.user, @model.name.constantize).resolve
137
137
  @current_action = @action
138
- if @action.parent == 'index'
138
+ if @action.parent == 'index'
139
139
  records = apply_scopes(records)
140
140
  @ar_object = filter_by(params, records, filter_params: @model.filter_params(params))
141
141
  else
@@ -150,7 +150,7 @@ module CmAdmin
150
150
  format.html { render @action.layout }
151
151
  end
152
152
  elsif @action.display_type == :page
153
- data = @action.parent == "index" ? @ar_object.data : @ar_object
153
+ @action.parent == 'index' ? @ar_object.data : @ar_object
154
154
  # TODO: To set a default value for @action.layout, Since it is used in render above,
155
155
  # Need to check and fix it.
156
156
  format.html { render @action.partial, layout: @action.layout || 'cm_admin' }
@@ -163,11 +163,11 @@ module CmAdmin
163
163
  redirect_url = @model.current_action.redirection_url || @action.redirection_url || request.referrer || "/cm_admin/#{@model.ar_model.table_name}/#{@response_object.id}"
164
164
  format.html { redirect_to redirect_url, notice: "#{@action.name.titleize} is successful" }
165
165
  else
166
- error_messages = response_object.errors.full_messages.map{|error_message| "<li>#{error_message}</li>"}.join
166
+ error_messages = response_object.errors.full_messages.map { |error_message| "<li>#{error_message}</li>" }.join
167
167
  format.html { redirect_to request.referrer, alert: "<b>#{@action.name.titleize} is unsuccessful</b><br /><ul>#{error_messages}</ul>" }
168
168
  end
169
- rescue => exception
170
- format.html { redirect_to request.referrer, alert: "<div><b>#{@action.name.titleize} is unsuccessful</b><br /><ul><li>#{exception.message}</li></ul></div>" }
169
+ rescue StandardError => e
170
+ format.html { redirect_to request.referrer, alert: "<div><b>#{@action.name.titleize} is unsuccessful</b><br /><ul><li>#{e.message}</li></ul></div>" }
171
171
  end
172
172
  end
173
173
  end
@@ -185,6 +185,12 @@ module CmAdmin
185
185
  end
186
186
  end
187
187
 
188
+ def reset_sort_columns
189
+ @model = Model.find_by({ name: controller_name.classify })
190
+ @model.default_sort_column = nil
191
+ @model.default_sort_direction = nil
192
+ end
193
+
188
194
  def get_nested_table_fields(fields)
189
195
  nested_table_fields = []
190
196
  fields.each do |field|
@@ -200,17 +206,17 @@ module CmAdmin
200
206
  def resource_identifier
201
207
  @ar_object, @associated_model, @associated_ar_object = custom_controller_action(action_name, params.permit!) if !@ar_object.present? && params[:id].present?
202
208
  authorize @ar_object, policy_class: "CmAdmin::#{controller_name.classify}Policy".constantize if defined? "CmAdmin::#{controller_name.classify}Policy".constantize
203
- aar_model = request.url.split('/')[-2].classify.constantize if params[:aar_id]
209
+ aar_model = request.url.split('/')[-2].classify.constantize if params[:aar_id]
204
210
  @associated_ar_object = fetch_ar_object(aar_model, params[:aar_id]) if params[:aar_id]
205
211
  nested_fields = get_nested_table_fields(@model.available_fields[:new])
206
212
  nested_fields += get_nested_table_fields(@model.available_fields[:edit])
207
213
  @reflections = @model.ar_model.reflect_on_all_associations
208
214
  nested_fields.each do |nested_field|
209
215
  table_name = nested_field.field_name
210
- reflection = @reflections.select {|x| x if x.name == table_name}.first
216
+ reflection = @reflections.select { |x| x if x.name == table_name }.first
211
217
  if reflection.macro == :has_many
212
- @ar_object.send(table_name).build if action_name == "new" || action_name == "edit"
213
- elsif action_name == "new"
218
+ @ar_object.send(table_name).build if action_name == 'new' || action_name == 'edit'
219
+ elsif action_name == 'new'
214
220
  @ar_object.send(('build_' + table_name.to_s).to_sym)
215
221
  end
216
222
  end
@@ -226,9 +232,7 @@ module CmAdmin
226
232
  else
227
233
  cm_admin.send("#{@model.name.underscore}_show_path", @ar_object)
228
234
  end
229
- if params['attachment_destroy_ids'].present?
230
- ActiveStorage::Attachment.where(id: params['attachment_destroy_ids']).destroy_all
231
- end
235
+ ActiveStorage::Attachment.where(id: params['attachment_destroy_ids']).destroy_all if params['attachment_destroy_ids'].present?
232
236
  format.html { redirect_to redirect_url, allow_other_host: true, notice: "#{action_name.titleize} #{@ar_object.class.name.downcase} is successful" }
233
237
  else
234
238
  format.html { render '/cm_admin/main/new', notice: "#{action_name.titleize} #{@ar_object.class.name.downcase} is unsuccessful" }
@@ -259,7 +263,7 @@ module CmAdmin
259
263
  else
260
264
  child_records
261
265
  end
262
- return @ar_object, @associated_model, @associated_ar_object
266
+ [@ar_object, @associated_model, @associated_ar_object]
263
267
  end
264
268
 
265
269
  def apply_scopes(records)
@@ -272,35 +276,32 @@ module CmAdmin
272
276
  def filter_by(params, records, parent_record: nil, filter_params: {}, sort_params: {})
273
277
  filtered_result = OpenStruct.new
274
278
  cm_model = @associated_model || @model
275
- db_columns = cm_model.ar_model&.columns&.map{|x| x.name.to_sym}
276
- if db_columns.include?(@current_action.sort_column)
277
- sort_column = @current_action.sort_column
278
- else
279
- sort_column = 'created_at'
280
- end
279
+ db_columns = cm_model.ar_model&.columns&.map { |x| x.name.to_sym }
280
+ sort_column = if db_columns.include?(params[:sort_column]&.to_sym)
281
+ params[:sort_column]
282
+ else
283
+ cm_model.default_sort_column
284
+ end
285
+ sort_direction = params[:sort_direction] || cm_model.default_sort_direction
281
286
  records = "CmAdmin::#{@model.name}Policy::#{@current_action.name.classify}Scope".constantize.new(Current.user, @model.name.constantize).resolve if records.nil?
282
- records = records.order("#{sort_column} #{@current_action.sort_direction}")
287
+ records = records.order("#{sort_column} #{sort_direction}") if sort_column.present?
283
288
  final_data = CmAdmin::Models::Filter.filtered_data(filter_params, records, cm_model.filters)
284
289
  pagy, records = pagy(final_data)
285
290
  filtered_result.data = records
286
291
  filtered_result.pagy = pagy
287
292
  filtered_result.parent_record = parent_record
288
293
  filtered_result.associated_model = @associated_model.name if @associated_model
289
- # filtered_result.ar_model =
290
- # filtered_result.facets = paginate(page, raw_data.size)
291
- # filtered_result.sort = sort_params
292
- # filtered_result.facets.sort = sort_params
293
- return filtered_result
294
+ filtered_result
294
295
  end
295
296
 
296
- def kanban_filter_by(params, records, filter_params={}, sort_params={})
297
+ def kanban_filter_by(params, records, filter_params = {}, _sort_params = {})
297
298
  filtered_result = OpenStruct.new
298
299
  cm_model = @associated_model || @model
299
- db_columns = cm_model.ar_model&.columns&.map{|x| x.name.to_sym}
300
+ db_columns = cm_model.ar_model&.columns&.map { |x| x.name.to_sym }
300
301
  if db_columns.include?(@current_action.sort_column)
301
- sort_column = @current_action.sort_column
302
+ @current_action.sort_column
302
303
  else
303
- sort_column = 'created_at'
304
+ 'created_at'
304
305
  end
305
306
  records = "CmAdmin::#{@model.name}Policy::Scope".constantize.new(Current.user, @model.name.constantize).resolve if records.nil?
306
307
  # records = records.order("#{sort_column} #{@current_action.sort_direction}")
@@ -313,7 +314,7 @@ module CmAdmin
313
314
  page = params[:page] || 1
314
315
  max_page = (group_record_count.values.max.to_i / per_page.to_f).ceil
315
316
  filtered_result.paging['next_page'] = (page.to_i < max_page)
316
- filtered_result.column_count = group_record_count.reject {|key, value| key.blank? }
317
+ filtered_result.column_count = group_record_count.reject { |key, _value| key.blank? }
317
318
 
318
319
  column_names = @model.ar_model.send(params[:kanban_column_name]&.pluralize || @current_action.kanban_attr[:column_name].pluralize).keys
319
320
  if @current_action.kanban_attr[:only].present?
@@ -326,8 +327,8 @@ module CmAdmin
326
327
  filtered_result.data[column] = ''
327
328
  next if page.to_i > (total_count.to_i / per_page.to_f).ceil
328
329
 
329
- pagy, records = pagy(final_data.send(column), items: per_page.to_i)
330
- filtered_result.data[column] = render_to_string partial: 'cm_admin/main/kanban_card', locals: { ar_collection: records}
330
+ _, records = pagy(final_data.send(column), items: per_page.to_i)
331
+ filtered_result.data[column] = render_to_string partial: 'cm_admin/main/kanban_card', locals: { ar_collection: records }
331
332
  end
332
333
  filtered_result
333
334
  end
@@ -340,8 +341,8 @@ module CmAdmin
340
341
  table_name = @model.ar_model.reflections.with_indifferent_access[nested_table_field.field_name.to_s].klass.table_name
341
342
  end
342
343
  column_names = table_name.to_s.classify.constantize.column_names
343
- column_names = column_names.map {|column_name| column_name.gsub('_cents', '') }
344
- column_names = column_names.reject { |column_name| CmAdmin::REJECTABLE_FIELDS.include?(column_name) }.map(&:to_sym) + [:id, :_destroy]
344
+ column_names = column_names.map { |column_name| column_name.gsub('_cents', '') }
345
+ column_names = column_names.reject { |column_name| CmAdmin::REJECTABLE_FIELDS.include?(column_name) }.map(&:to_sym) + %i[id _destroy]
345
346
  if nested_table_field.associated_fields
346
347
  nested_table_field.associated_fields.each do |associated_field|
347
348
  column_names << generate_nested_params(associated_field)
@@ -355,17 +356,17 @@ module CmAdmin
355
356
  end
356
357
 
357
358
  def resource_params(params)
358
- columns = @model.ar_model.columns_hash.map {|key, ar_adapter|
359
+ columns = @model.ar_model.columns_hash.map do |_key, ar_adapter|
359
360
  ar_adapter.sql_type_metadata.sql_type.ends_with?('[]') ? Hash[ar_adapter.name, []] : ar_adapter.name.to_sym
360
- }
361
+ end
361
362
  columns += @model.ar_model.stored_attributes.values.flatten
362
363
  permittable_fields = @model.additional_permitted_fields + columns.reject { |i| CmAdmin::REJECTABLE_FIELDS.include?(i) }
363
364
  permittable_fields += attachment_fields(@model.ar_model.name.constantize)
364
365
  nested_table_fields = get_nested_table_fields(@model.available_fields[:new])
365
366
  nested_table_fields += get_nested_table_fields(@model.available_fields[:edit])
366
- nested_fields = nested_table_fields.uniq.map {|nested_table_field|
367
+ nested_fields = nested_table_fields.uniq.map do |nested_table_field|
367
368
  generate_nested_params(nested_table_field)
368
- }
369
+ end
369
370
  permittable_fields += nested_fields
370
371
  @model.ar_model.columns.map { |col| permittable_fields << col.name.split('_cents') if col.name.include?('_cents') }
371
372
  params.require(@model.name.underscore.to_sym).permit(*permittable_fields)
@@ -380,14 +381,15 @@ module CmAdmin
380
381
  private
381
382
 
382
383
  def attachment_fields(model_object)
383
- model_object.reflect_on_all_associations.map {|reflection|
384
+ model_object.reflect_on_all_associations.map do |reflection|
384
385
  next if reflection.options[:polymorphic]
386
+
385
387
  if reflection.class.name.include?('HasOne')
386
388
  reflection.name.to_s.gsub('_attachment', '').gsub('rich_text_', '').to_sym
387
389
  elsif reflection.class.name.include?('HasMany')
388
390
  Hash[reflection.name.to_s.gsub('_attachments', ''), []]
389
391
  end
390
- }.compact
392
+ end.compact
391
393
  end
392
394
  end
393
395
  end
@@ -3,18 +3,16 @@
3
3
  .cm-index-page__filters
4
4
  == render partial: 'cm_admin/main/filters', locals: { filters: @associated_model.filters }
5
5
  p.table-top__total-count = "#{humanized_ar_collection_count(@associated_ar_object.pagy.count, @action.child_records.to_s)}"
6
- .table-top__column-action
7
- -
8
- - if @associated_model && @associated_model.available_actions.map(&:name).include?('new') && has_valid_policy(@associated_ar_object, 'new')
9
- - association = @ar_object.class.reflect_on_all_associations.select{|x| x.name == @associated_model.name.tableize.to_sym }.first
10
- - polymorphic_name = (association && association.inverse_of && association.inverse_of.options[:polymorphic]) ? association.inverse_of.name : ''
11
- a href="#{CmAdmin::Engine.mount_path}/#{@associated_model.name.tableize}/new?associated_id=#{@ar_object.id}&associated_class=#{@ar_object.class.name.underscore}&polymorphic_name=#{polymorphic_name}&referrer=#{request.path}"
12
- button.btn-secondary Add
13
- / button.secondary-btn.column-btn data-target="#columnActionModal" data-toggle="modal" type="button"
14
- / span
15
- / i.fa.fa-columns.bolder
16
- / span
17
- / i.fa.fa-angle-down
6
+ div.d-flex.gap-3
7
+ .table-top__column-action
8
+ - if @associated_model && @associated_model.available_actions.map(&:name).include?('new') && has_valid_policy(@associated_ar_object, 'new')
9
+ - association = @ar_object.class.reflect_on_all_associations.select{|x| x.name == @associated_model.name.tableize.to_sym }.first
10
+ - polymorphic_name = (association && association.inverse_of && association.inverse_of.options[:polymorphic]) ? association.inverse_of.name : ''
11
+ a href="#{CmAdmin::Engine.mount_path}/#{@associated_model.name.tableize}/new?associated_id=#{@ar_object.id}&associated_class=#{@ar_object.class.name.underscore}&polymorphic_name=#{polymorphic_name}&referrer=#{request.path}"
12
+ button.btn-secondary Add
13
+ - if @associated_model.sort_columns.present?
14
+ = render 'cm_admin/main/sort', model: @associated_model, ar_object: @associated_ar_object
15
+
18
16
  - if flash[:alert].present?
19
17
  .alert.alert-danger role="alert"
20
18
  = flash[:alert].html_safe
@@ -13,7 +13,7 @@
13
13
  - nested_table_field.fields.each do |field|
14
14
  th data-field-type="#{field.input_type}"
15
15
  - if field.label
16
- = field.label.to_s.titleize || field.field_name.to_s.titleize
16
+ = field.label.to_s || field.field_name.to_s.titleize
17
17
  tbody class="insert-cocoon-position-#{uniq_no}"
18
18
  = f.fields_for table_name do |record|
19
19
  - if record.object.persisted? || @ar_object.errors.present?
@@ -0,0 +1,27 @@
1
+ - sort_column = params[:sort_column] || model.default_sort_column
2
+ - sort_direction = params[:sort_direction] || model.default_sort_direction
3
+ .dropdown
4
+ button.sort-button data-bs-toggle="dropdown"
5
+ - if sort_column.present?
6
+ span
7
+ - if sort_direction == 'asc'
8
+ i.fa-duotone.fa-solid.fa-arrow-up-short-wide
9
+ - else
10
+ i.fa-duotone.fa-solid.fa-arrow-down-short-wide
11
+ = "#{model.sort_columns.find{ |c| c[:column] == sort_column }[:display_name]}"
12
+ - else
13
+ span
14
+ i.fa-solid.fa-arrow-down-arrow-up
15
+ | Sort
16
+ .dropdown-menu.sort-menu
17
+ div.sort-menu__top
18
+ = select_tag 'sort_column', options_for_select([['Select Column', '']] + model.sort_columns.map { |column| [column[:display_name], column[:column]] }, selected: sort_column), { class: 'form-select form-select-sm', data: { behaviour: 'sort-column' } }
19
+ .div.btn-group role="group"
20
+ input.btn-check type="radio" name="sort_direction" id="asc" value="asc" autocomplete="off" data-behaviour="sort-direction" checked=(sort_direction.blank? ? true : (sort_direction == 'asc' ? true : nil))
21
+ label.btn.btn-outline-dark.btn-sm for="asc" Ascending
22
+
23
+ input.btn-check type="radio" name="sort_direction" id="desc" value="desc" autocomplete="off" data-behaviour="sort-direction" checked=(sort_direction == 'desc' ? true : nil)
24
+ label.btn.btn-outline-dark.btn-sm for="desc" Descending
25
+ .sort-menu__bottom
26
+ button.btn.btn-sm.btn-ghost data-behaviour="reset-sort"
27
+ | Reset
@@ -7,11 +7,10 @@
7
7
  i.fa.fa-table
8
8
  a.btn.btn-ghost href="#{cm_admin.send("#{@model.name.underscore}_index_path")}?page=#{params[:page] || 1}&view_type=card"
9
9
  i.fa.fa-table-cells
10
- / button.secondary-btn.column-btn data-bs-target="#columnActionModal" data-bs-toggle="modal"
11
- / span
12
- / i.fa.fa-columns.bolder
13
- / span
14
- / i.fa.fa-angle-down
10
+
11
+ - if @model.sort_columns.present?
12
+ = render 'cm_admin/main/sort', model: @model, ar_object: @ar_object
13
+
15
14
  - if flash[:alert].present?
16
15
  .alert.alert-danger.me-4 role="alert"
17
16
  = flash[:alert].html_safe
@@ -29,6 +29,7 @@ html
29
29
  = javascript_importmap_tags
30
30
  = javascript_import_module_tag "cm_admin/application"
31
31
  = javascript_include_tag 'cm_admin/custom', 'data-turbolinks-track': 'reload'
32
+ script src="https://kit.fontawesome.com/9c93dde7a7.js" data-mutate-approach="sync"
32
33
  = stylesheet_link_tag 'cm_admin/custom', media: 'all', 'data-turbolinks-track': 'reload'
33
34
  link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" /
34
35
 
data/config/importmap.rb CHANGED
@@ -7,7 +7,6 @@ pin 'jgrowl', to: 'https://ga.jspm.io/npm:jgrowl@1.4.8/jquery.jgrowl.js'
7
7
  pin 'moment', to: 'https://ga.jspm.io/npm:moment@2.29.4/moment.js'
8
8
  pin 'trix', to: 'https://ga.jspm.io/npm:trix@2.1.3/dist/trix.esm.min.js'
9
9
  pin '@rails/actiontext', to: 'https://ga.jspm.io/npm:@rails/actiontext@7.1.3-4/app/assets/javascripts/actiontext.esm.js'
10
- pin '@fortawesome/fontawesome-free', to: 'https://ga.jspm.io/npm:@fortawesome/fontawesome-free@6.1.1/js/all.js'
11
10
  pin 'daterangepicker', to: 'https://ga.jspm.io/npm:daterangepicker@3.1.0/daterangepicker.js'
12
11
  pin '@nathanvda/cocoon', to: 'https://ga.jspm.io/npm:@nathanvda/cocoon@1.2.14/cocoon.js'
13
12
  pin 'select2', to: 'https://ga.jspm.io/npm:select2@4.1.0-rc.0/dist/js/select2.js'
data/config/routes.rb CHANGED
@@ -17,7 +17,13 @@ CmAdmin::Engine.routes.draw do
17
17
  end
18
18
  end
19
19
 
20
- model.available_actions.sort_by {|act| act.name}.each do |act|
20
+ if model.sort_columns.present?
21
+ scope model.name.tableize do
22
+ send(:post, 'reset_sort_columns', to: "#{model.name.underscore}#reset_sort_columns", as: "#{model.name.underscore}_reset_sort_columns")
23
+ end
24
+ end
25
+
26
+ model.available_actions.sort_by { |act| act.name }.each do |act|
21
27
  scope model.name.tableize do
22
28
  # Define route only when action trail related field is present
23
29
  if act.name == 'history'