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 +4 -4
- data/Gemfile.lock +1 -1
- data/app/assets/javascripts/cm_admin/filters.js +30 -3
- data/app/assets/javascripts/cm_admin/scaffolds.js +0 -1
- data/app/assets/stylesheets/cm_admin/base/filters.scss +20 -0
- data/app/assets/stylesheets/cm_admin/cm_admin.css.scss +0 -1
- data/app/controllers/cm_admin/resource_controller.rb +65 -62
- data/app/models/concerns/cm_admin/cm_role.rb +10 -6
- data/app/views/cm_admin/main/_associated_table.html.slim +10 -12
- data/app/views/cm_admin/main/_nested_table_form.html.slim +1 -1
- data/app/views/cm_admin/main/_sort.html.slim +27 -0
- data/app/views/cm_admin/main/_table.html.slim +4 -5
- data/app/views/cm_admin/roles/permissions.html.slim +2 -1
- data/app/views/layouts/cm_admin.html.slim +1 -0
- data/config/importmap.rb +0 -1
- data/config/routes.rb +7 -1
- data/docs/RoleManagement.md +36 -0
- data/lib/cm_admin/model.rb +85 -77
- data/lib/cm_admin/models/dsl_method.rb +23 -0
- data/lib/cm_admin/version.rb +1 -1
- data/lib/cm_admin/view_helpers/page_info_helper.rb +2 -1
- metadata +3 -3
- data/app/assets/stylesheets/cm_admin/dependency/fontawesome.all.css +0 -7831
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ab22f4bef2b77930d17a0910dbc5ee20f0773f9f025d7e7ba7fde8a55be7e5f8
|
4
|
+
data.tar.gz: ba334104c29113fbec5440dbe31b13b8fb8513efd6a8be7308928815141256a0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 67c7bf36f684d04d976fbad85a58f5910a84762f2e750918b3ab2eef6b7c9b4471bb9ff6f8b1594bb8dc7f3bf9910cf6b4560d9e80ad8ae24d6a8b56321901ae
|
7
|
+
data.tar.gz: 47b9d696e72e73ef748ffc51301891dc2fa3667f3f201ec61643167d13b286d0dc289ec7ddd4a867700c93a1b23bdbb5395b747d82e7fc71e00c39fc42e77e44
|
data/Gemfile.lock
CHANGED
@@ -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
|
+
});
|
@@ -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
|
+
}
|
@@ -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::
|
14
|
+
records = "CmAdmin::#{@model.name}Policy::IndexScope".constantize.new(Current.user, @model.name.constantize).resolve
|
13
15
|
records = apply_scopes(records)
|
14
|
-
if
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
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::
|
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(
|
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: [
|
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
|
119
|
+
error_messages = @bulk_action_processor.invalid_records.map do |invalid_record|
|
120
120
|
"<li>#{invalid_record.error_message}</li>"
|
121
|
-
|
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
|
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
|
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
|
-
|
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 =>
|
170
|
-
format.html { redirect_to request.referrer, alert: "<div><b>#{@action.name.titleize} is unsuccessful</b><br /><ul><li>#{
|
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
|
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
|
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 ==
|
213
|
-
elsif action_name ==
|
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
|
-
|
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
|
-
|
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?(
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
records =
|
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
|
-
|
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={},
|
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
|
-
|
302
|
+
@current_action.sort_column
|
301
303
|
else
|
302
|
-
|
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,
|
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
|
-
|
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) + [
|
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
|
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
|
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
|
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
|
-
|
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
|
6
|
+
set_icon 'fa fa-database'
|
7
7
|
cm_index do
|
8
|
-
page_title '
|
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
|
-
|
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
|
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
|
-
.
|
7
|
-
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
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
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
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.
|
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.
|
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'
|