cm-admin 2.1.4 → 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: ea557439972b4bf69c93412b81415aed76726933fde6e45599ee6f08bdc9e5be
4
- data.tar.gz: f1b704b48795f36a0b8efe342dc4f4de49351112c47a2e6fd6dfbec982fa5bc1
3
+ metadata.gz: ab22f4bef2b77930d17a0910dbc5ee20f0773f9f025d7e7ba7fde8a55be7e5f8
4
+ data.tar.gz: ba334104c29113fbec5440dbe31b13b8fb8513efd6a8be7308928815141256a0
5
5
  SHA512:
6
- metadata.gz: ce7a4d687dcbcd2db7eafaaf77fa45f0d0048b3ba3bfef259c5eb9c7510a569d7c2d9e4876342973d343aca20d295959df33455dbd150e684bea4deb17418f7b
7
- data.tar.gz: 4c823776edeb548873d43caf016c402cf102eac48d41b5c9d94e6a7bfd8fbd31ab61dea523215d3d55192312387c0f56465bd5591fb6461f69a771240a81d411
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.4)
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
- records = "CmAdmin::#{@model.name}Policy::Scope".constantize.new(Current.user, @model.name.constantize).resolve
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 }
@@ -31,7 +33,7 @@ module CmAdmin
31
33
 
32
34
  def cm_show(params)
33
35
  @current_action = CmAdmin::Models::Action.find_by(@model, name: 'show')
34
- scoped_model = "CmAdmin::#{@model.name}Policy::Scope".constantize.new(Current.user, @model.name.constantize).resolve
36
+ scoped_model = "CmAdmin::#{@model.name}Policy::ShowScope".constantize.new(Current.user, @model.name.constantize).resolve
35
37
  @ar_object = fetch_ar_object(scoped_model, params[:id])
36
38
  @alerts = @model.alerts
37
39
  resource_identifier
@@ -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
@@ -133,9 +133,9 @@ module CmAdmin
133
133
  end
134
134
 
135
135
  def cm_custom_method(params)
136
- records = "CmAdmin::#{@model.name}Policy::Scope".constantize.new(Current.user, @model.name.constantize).resolve
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
@@ -175,7 +175,7 @@ module CmAdmin
175
175
  end
176
176
 
177
177
  def cm_custom_action_modal(params)
178
- scoped_model = "CmAdmin::#{@model.name}Policy::Scope".constantize.new(Current.user, @model.name.constantize).resolve
178
+ scoped_model = "CmAdmin::#{@model.name}Policy::#{params[:action_name].classify}Scope".constantize.new(Current.user, @model.name.constantize).resolve
179
179
  @ar_object = fetch_ar_object(scoped_model, params[:id])
180
180
  if params[:action_name] == 'destroy'
181
181
  render partial: '/layouts/destroy_action_modal', locals: { ar_object: @ar_object }
@@ -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" }
@@ -240,7 +244,8 @@ module CmAdmin
240
244
  @current_action = CmAdmin::Models::Action.find_by(@model, name: action_name.to_s)
241
245
  return unless @current_action
242
246
 
243
- @ar_object = fetch_ar_object(@model.ar_model.name.classify.constantize, params[:id])
247
+ scoped_model = "CmAdmin::#{@model.name}Policy::#{action_name.classify}Scope".constantize.new(Current.user, @model.ar_model.name.classify.constantize).resolve
248
+ @ar_object = fetch_ar_object(scoped_model, params[:id])
244
249
  return @ar_object unless @current_action.child_records
245
250
 
246
251
  child_records = @ar_object.send(@current_action.child_records)
@@ -258,7 +263,7 @@ module CmAdmin
258
263
  else
259
264
  child_records
260
265
  end
261
- return @ar_object, @associated_model, @associated_ar_object
266
+ [@ar_object, @associated_model, @associated_ar_object]
262
267
  end
263
268
 
264
269
  def apply_scopes(records)
@@ -271,35 +276,32 @@ module CmAdmin
271
276
  def filter_by(params, records, parent_record: nil, filter_params: {}, sort_params: {})
272
277
  filtered_result = OpenStruct.new
273
278
  cm_model = @associated_model || @model
274
- db_columns = cm_model.ar_model&.columns&.map{|x| x.name.to_sym}
275
- if db_columns.include?(@current_action.sort_column)
276
- sort_column = @current_action.sort_column
277
- else
278
- sort_column = 'created_at'
279
- end
280
- records = "CmAdmin::#{@model.name}Policy::Scope".constantize.new(Current.user, @model.name.constantize).resolve if records.nil?
281
- records = records.order("#{sort_column} #{@current_action.sort_direction}")
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
286
+ records = "CmAdmin::#{@model.name}Policy::#{@current_action.name.classify}Scope".constantize.new(Current.user, @model.name.constantize).resolve if records.nil?
287
+ records = records.order("#{sort_column} #{sort_direction}") if sort_column.present?
282
288
  final_data = CmAdmin::Models::Filter.filtered_data(filter_params, records, cm_model.filters)
283
289
  pagy, records = pagy(final_data)
284
290
  filtered_result.data = records
285
291
  filtered_result.pagy = pagy
286
292
  filtered_result.parent_record = parent_record
287
293
  filtered_result.associated_model = @associated_model.name if @associated_model
288
- # filtered_result.ar_model =
289
- # filtered_result.facets = paginate(page, raw_data.size)
290
- # filtered_result.sort = sort_params
291
- # filtered_result.facets.sort = sort_params
292
- return filtered_result
294
+ filtered_result
293
295
  end
294
296
 
295
- def kanban_filter_by(params, records, filter_params={}, sort_params={})
297
+ def kanban_filter_by(params, records, filter_params = {}, _sort_params = {})
296
298
  filtered_result = OpenStruct.new
297
299
  cm_model = @associated_model || @model
298
- 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 }
299
301
  if db_columns.include?(@current_action.sort_column)
300
- sort_column = @current_action.sort_column
302
+ @current_action.sort_column
301
303
  else
302
- sort_column = 'created_at'
304
+ 'created_at'
303
305
  end
304
306
  records = "CmAdmin::#{@model.name}Policy::Scope".constantize.new(Current.user, @model.name.constantize).resolve if records.nil?
305
307
  # records = records.order("#{sort_column} #{@current_action.sort_direction}")
@@ -312,7 +314,7 @@ module CmAdmin
312
314
  page = params[:page] || 1
313
315
  max_page = (group_record_count.values.max.to_i / per_page.to_f).ceil
314
316
  filtered_result.paging['next_page'] = (page.to_i < max_page)
315
- 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? }
316
318
 
317
319
  column_names = @model.ar_model.send(params[:kanban_column_name]&.pluralize || @current_action.kanban_attr[:column_name].pluralize).keys
318
320
  if @current_action.kanban_attr[:only].present?
@@ -325,8 +327,8 @@ module CmAdmin
325
327
  filtered_result.data[column] = ''
326
328
  next if page.to_i > (total_count.to_i / per_page.to_f).ceil
327
329
 
328
- pagy, records = pagy(final_data.send(column), items: per_page.to_i)
329
- 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 }
330
332
  end
331
333
  filtered_result
332
334
  end
@@ -339,8 +341,8 @@ module CmAdmin
339
341
  table_name = @model.ar_model.reflections.with_indifferent_access[nested_table_field.field_name.to_s].klass.table_name
340
342
  end
341
343
  column_names = table_name.to_s.classify.constantize.column_names
342
- column_names = column_names.map {|column_name| column_name.gsub('_cents', '') }
343
- 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]
344
346
  if nested_table_field.associated_fields
345
347
  nested_table_field.associated_fields.each do |associated_field|
346
348
  column_names << generate_nested_params(associated_field)
@@ -354,17 +356,17 @@ module CmAdmin
354
356
  end
355
357
 
356
358
  def resource_params(params)
357
- columns = @model.ar_model.columns_hash.map {|key, ar_adapter|
359
+ columns = @model.ar_model.columns_hash.map do |_key, ar_adapter|
358
360
  ar_adapter.sql_type_metadata.sql_type.ends_with?('[]') ? Hash[ar_adapter.name, []] : ar_adapter.name.to_sym
359
- }
361
+ end
360
362
  columns += @model.ar_model.stored_attributes.values.flatten
361
363
  permittable_fields = @model.additional_permitted_fields + columns.reject { |i| CmAdmin::REJECTABLE_FIELDS.include?(i) }
362
364
  permittable_fields += attachment_fields(@model.ar_model.name.constantize)
363
365
  nested_table_fields = get_nested_table_fields(@model.available_fields[:new])
364
366
  nested_table_fields += get_nested_table_fields(@model.available_fields[:edit])
365
- nested_fields = nested_table_fields.uniq.map {|nested_table_field|
367
+ nested_fields = nested_table_fields.uniq.map do |nested_table_field|
366
368
  generate_nested_params(nested_table_field)
367
- }
369
+ end
368
370
  permittable_fields += nested_fields
369
371
  @model.ar_model.columns.map { |col| permittable_fields << col.name.split('_cents') if col.name.include?('_cents') }
370
372
  params.require(@model.name.underscore.to_sym).permit(*permittable_fields)
@@ -379,14 +381,15 @@ module CmAdmin
379
381
  private
380
382
 
381
383
  def attachment_fields(model_object)
382
- model_object.reflect_on_all_associations.map {|reflection|
384
+ model_object.reflect_on_all_associations.map do |reflection|
383
385
  next if reflection.options[:polymorphic]
386
+
384
387
  if reflection.class.name.include?('HasOne')
385
388
  reflection.name.to_s.gsub('_attachment', '').gsub('rich_text_', '').to_sym
386
389
  elsif reflection.class.name.include?('HasMany')
387
390
  Hash[reflection.name.to_s.gsub('_attachments', ''), []]
388
391
  end
389
- }.compact
392
+ end.compact
390
393
  end
391
394
  end
392
395
  end
@@ -3,9 +3,10 @@ module CmAdmin::CmRole
3
3
  included do
4
4
  cm_admin do
5
5
  actions only: []
6
- set_icon "fa fa-database"
6
+ set_icon 'fa fa-database'
7
7
  cm_index do
8
- page_title 'Role'
8
+ page_title 'Roles & Permissions'
9
+ set_display_name 'Role'
9
10
 
10
11
  filter [:name], :search, placeholder: 'Search'
11
12
 
@@ -20,10 +21,13 @@ module CmAdmin::CmRole
20
21
  # allowed_params = params.permit(role_permission: []).to_h
21
22
  @role = CmRole.find(params[:id])
22
23
  params[:role_permission].except(:submit).each do |model_name, action_arr|
23
- @role.cm_permissions.where(ar_model_name: model_name).where.not(action_name: action_arr.select{ |k,v| k if v.has_key?('is_checked')}.keys).destroy_all
24
+ action_names = action_arr.select { |k, v| k if v.key?('is_checked') }.keys
25
+ action_names << 'create' if action_names.include?('new')
26
+ action_names << 'update' if action_names.include?('edit')
27
+ @role.cm_permissions.where(ar_model_name: model_name).where.not(action_name: action_names).destroy_all
24
28
  action_arr.each do |action_name, selected_option|
25
29
  if selected_option.has_key?('is_checked')
26
- permission = @role.cm_permissions.where(action_name: action_name, ar_model_name: model_name).first_or_create
30
+ permission = @role.cm_permissions.where(action_name:, ar_model_name: model_name).first_or_create
27
31
  permission.update(scope_name: selected_option['scope_name'])
28
32
  end
29
33
  end
@@ -34,7 +38,7 @@ module CmAdmin::CmRole
34
38
  cm_show_section 'Role details' do
35
39
  field :name
36
40
  field :created_at, field_type: :date, format: '%d %b, %Y'
37
- field :updated_at, field_type: :date, format: '%d %b, %Y'
41
+ field :updated_at, field_type: :date, format: '%d %b, %Y'
38
42
  end
39
43
  end
40
44
  tab :permissions, 'permissions', layout_type: 'cm_association_show', partial: '/cm_admin/roles/permissions'
@@ -53,4 +57,4 @@ module CmAdmin::CmRole
53
57
  end
54
58
  end
55
59
  end
56
- end
60
+ 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
@@ -2,7 +2,8 @@
2
2
  .form-page__body
3
3
  .form-container
4
4
  = form_for CmPermission.new, url: cm_admin.send('cm_role_create_role_permission_path', @ar_object), method: :post do |f|
5
- - CmAdmin.config.cm_admin_models.each do |model|
5
+ - cm_models = CmAdmin.config.cm_admin_models.sort_by{|k, v| k.display_name}
6
+ - cm_models.each do |model|
6
7
  - next if model.override_policy == true
7
8
  .row
8
9
  .col.form-container
@@ -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'