cm-admin 3.0.14 → 3.0.16
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/custom_action.js +5 -1
- data/app/assets/javascripts/cm_admin/filters.js +351 -239
- data/app/assets/javascripts/cm_admin/shared_scaffolds.js +7 -1
- data/app/assets/stylesheets/cm_admin/base/filters.scss +2 -4
- data/app/assets/stylesheets/cm_admin/base/scaffold.scss +4 -0
- data/app/controllers/cm_admin/static_controller.rb +7 -4
- data/app/models/cm_role.rb +13 -0
- data/app/models/concerns/cm_admin/cm_role.rb +3 -1
- data/app/views/cm_admin/main/_card.html.slim +2 -1
- data/app/views/cm_admin/main/_kanban.html.slim +4 -1
- data/app/views/cm_admin/main/_table.html.slim +3 -0
- data/app/views/cm_admin/main/index.html.slim +0 -3
- data/docs/CustomFilterMethod.md +53 -0
- data/docs/RoleManagement.md +22 -7
- data/lib/cm_admin/version.rb +1 -1
- data/lib/cm_admin/view_helpers.rb +1 -1
- data/lib/generators/cm_admin/install_role_generator.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f3debddacbd593b7ae649770a5237ea0af2f7c3481cfdb75f93adf833345c702
|
4
|
+
data.tar.gz: f2d63c47f670a66b78241fa45398edfac023c36acc89d079065734319e72bccd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7d04f7523d8e25ef11d45025a4e8adbb3f583c4d1c5b8e353cd30a7f0d055a79280cfe081a1120721f83377476ba074334d9ec01e822595b372e1c78f2c64217
|
7
|
+
data.tar.gz: 76b3873cc8a0bea1c6ec2bd435538c9e06304310000a77d7380e2fc9bbb46232734a6d1881ccb566ca7aa135679606dea7b374777b292092f1747a2736cc8adc
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import * as bootstrap from "bootstrap";
|
2
2
|
window.bootstrap = bootstrap;
|
3
3
|
|
4
|
-
|
4
|
+
export function handleFetchModalClick() {
|
5
5
|
$('[data-action="fetch-modal"]').on("click", function (e) {
|
6
6
|
const actionName = $(this).attr("data-action_name");
|
7
7
|
const modelName = $(this).attr("data-model_name");
|
@@ -27,4 +27,8 @@ document.addEventListener("turbo:load", function () {
|
|
27
27
|
},
|
28
28
|
});
|
29
29
|
});
|
30
|
+
}
|
31
|
+
|
32
|
+
document.addEventListener("turbo:load", function () {
|
33
|
+
handleFetchModalClick();
|
30
34
|
});
|
@@ -1,38 +1,48 @@
|
|
1
|
+
import { handleFetchModalClick } from "./custom_action";
|
2
|
+
|
1
3
|
var currentRequest = null;
|
2
4
|
|
3
5
|
var CmFilter = {
|
4
6
|
// Generate or remove elements of the dropdown based on the search value.
|
5
|
-
dropdown_search: function(element) {
|
6
|
-
if(element.val()) {
|
7
|
+
dropdown_search: function (element) {
|
8
|
+
if (element.val()) {
|
7
9
|
var filter = element.val().toUpperCase();
|
8
|
-
var dropdownElements = element
|
10
|
+
var dropdownElements = element
|
11
|
+
.parents(":nth(1)")
|
12
|
+
.find(".list-area")
|
13
|
+
.children();
|
9
14
|
for (var i = 0; i < dropdownElements.length; i++) {
|
10
15
|
var txtValue = $(dropdownElements[i]).children().text();
|
11
16
|
if (txtValue.toUpperCase().indexOf(filter) > -1) {
|
12
|
-
$(dropdownElements[i]).css(
|
17
|
+
$(dropdownElements[i]).css("display", "flex");
|
13
18
|
} else {
|
14
|
-
$(dropdownElements[i]).css(
|
19
|
+
$(dropdownElements[i]).css("display", "none");
|
15
20
|
}
|
16
21
|
}
|
17
22
|
}
|
18
|
-
}
|
19
|
-
}
|
23
|
+
},
|
24
|
+
};
|
20
25
|
|
21
26
|
// Main method which will structure the existing filter values with the newly
|
22
27
|
// applied filter. Send and receive the value from the backend.
|
23
|
-
var getFilteredData = function(
|
28
|
+
var getFilteredData = function (
|
29
|
+
filterType,
|
30
|
+
filterValue,
|
31
|
+
filterColumn = null,
|
32
|
+
sortData = null
|
33
|
+
) {
|
24
34
|
let sortColumn = $('[data-behaviour="sort-column"]').val();
|
25
35
|
let sortDirection = $('[data-behaviour="sort-direction"]:checked').val();
|
26
36
|
|
27
|
-
var url = window.location.pathname
|
37
|
+
var url = window.location.pathname;
|
28
38
|
|
29
39
|
// Based on the value changed for recent filter generate the filterParams hash
|
30
40
|
var filterParams = {};
|
31
41
|
if (filterColumn) {
|
32
42
|
filterParams[filterType] = {};
|
33
|
-
filterParams[filterType][filterColumn] = filterValue
|
43
|
+
filterParams[filterType][filterColumn] = filterValue;
|
34
44
|
} else {
|
35
|
-
if (filterType) filterParams[filterType] = filterValue
|
45
|
+
if (filterType) filterParams[filterType] = filterValue;
|
36
46
|
}
|
37
47
|
|
38
48
|
// page params is reinitialized to 1 when any new filter value is applied so
|
@@ -42,139 +52,172 @@ var getFilteredData = function(filterType, filterValue, filterColumn=null, sortD
|
|
42
52
|
filters: filterParams,
|
43
53
|
sort_column: sortColumn,
|
44
54
|
sort_direction: sortDirection,
|
45
|
-
page: 1
|
55
|
+
page: 1,
|
46
56
|
};
|
47
57
|
|
48
58
|
// Generate the queryString by concatenating the filterParams and
|
49
59
|
// searchParams that are already applied, if searchParams are present.
|
50
|
-
var searchParams = window.location.search
|
51
|
-
var searchParamsHash = getParamsAsObject(searchParams)
|
60
|
+
var searchParams = window.location.search;
|
61
|
+
var searchParamsHash = getParamsAsObject(searchParams);
|
52
62
|
if (Object.keys(searchParamsHash).length > 0) {
|
53
63
|
// Delete the previous applied value for multi_select filter from the
|
54
64
|
// searchParams as altering the array with new and old value will create
|
55
65
|
// more complicated logic. The new value is passed and structured in
|
56
66
|
// filterParams and will be concadinated with the searchParams post deletion
|
57
|
-
if (filterType ==
|
58
|
-
searchParams = getParamsAsObject(searchParams)
|
59
|
-
if (
|
60
|
-
|
67
|
+
if (filterType == "multi_select") {
|
68
|
+
searchParams = getParamsAsObject(searchParams);
|
69
|
+
if (
|
70
|
+
searchParams["filters"] &&
|
71
|
+
searchParams["filters"][filterType] !== undefined
|
72
|
+
) {
|
73
|
+
delete searchParams["filters"][filterType][filterColumn];
|
61
74
|
}
|
62
|
-
searchParams = jQuery.param(searchParams)
|
75
|
+
searchParams = jQuery.param(searchParams);
|
63
76
|
}
|
64
|
-
filterParams = jQuery.param(queryString)
|
65
|
-
var availableParams = searchParams +
|
66
|
-
var queryString = getParamsAsObject(availableParams)
|
77
|
+
filterParams = jQuery.param(queryString);
|
78
|
+
var availableParams = searchParams + "&" + filterParams;
|
79
|
+
var queryString = getParamsAsObject(availableParams);
|
67
80
|
}
|
68
|
-
|
81
|
+
|
69
82
|
if (filterColumn) {
|
70
83
|
const filterChip = $(`[data-db-column="${filterColumn}"]`);
|
71
|
-
const filterName = filterChip.find(
|
72
|
-
|
84
|
+
const filterName = filterChip.find(".filter-name");
|
85
|
+
|
73
86
|
if (isValueEmpty(filterValue)) {
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
87
|
+
if (!filterName.data("appended")) {
|
88
|
+
filterName.html(`${filterName.text()} is `);
|
89
|
+
filterName.data("appended", true);
|
90
|
+
}
|
78
91
|
} else {
|
79
|
-
|
80
|
-
|
92
|
+
filterName.html(filterName.text().replace(/\s*is $/, ""));
|
93
|
+
filterName.data("appended", false);
|
81
94
|
}
|
82
95
|
}
|
83
96
|
|
84
|
-
return currentRequest = $.ajax(url, {
|
85
|
-
type:
|
97
|
+
return (currentRequest = $.ajax(url, {
|
98
|
+
type: "GET",
|
86
99
|
data: queryString,
|
87
|
-
beforeSend: function() {
|
100
|
+
beforeSend: function () {
|
88
101
|
if (currentRequest !== null) {
|
89
102
|
currentRequest.abort();
|
90
103
|
}
|
91
104
|
},
|
92
|
-
success: function(data) {
|
93
|
-
var queryParam = jQuery.param(queryString)
|
94
|
-
window.history.pushState("", "", url +
|
95
|
-
if ($(
|
96
|
-
$.each(data.table.data, function(key, value) {
|
97
|
-
$(
|
105
|
+
success: function (data) {
|
106
|
+
var queryParam = jQuery.param(queryString);
|
107
|
+
window.history.pushState("", "", url + "?" + queryParam);
|
108
|
+
if ($("#view_type").val() == "kanban") {
|
109
|
+
$.each(data.table.data, function (key, value) {
|
110
|
+
$("." + key + " .cards").html(value);
|
98
111
|
});
|
99
|
-
$(
|
100
|
-
$.each(data.table.column_count, function(key, value) {
|
101
|
-
$(
|
112
|
+
$(".kanban-list .counter").html(0);
|
113
|
+
$.each(data.table.column_count, function (key, value) {
|
114
|
+
$("." + key + " .counter").html(value);
|
102
115
|
});
|
103
116
|
} else {
|
104
|
-
$(
|
117
|
+
$(".cm-index-page__table-container").html(data);
|
105
118
|
}
|
106
119
|
if (sortData) {
|
107
|
-
const dropdownElement = document.querySelector(
|
120
|
+
const dropdownElement = document.querySelector(
|
121
|
+
'[data-behaviour="sort-button-toggle"]'
|
122
|
+
);
|
108
123
|
if (dropdownElement) {
|
109
124
|
const dropdown = new bootstrap.Dropdown(dropdownElement);
|
110
125
|
dropdown.show();
|
111
126
|
}
|
112
127
|
}
|
128
|
+
handleFetchModalClick();
|
113
129
|
},
|
114
|
-
error: function(jqxhr, textStatus, errorThrown) {
|
130
|
+
error: function (jqxhr, textStatus, errorThrown) {
|
115
131
|
console.log(errorThrown, textStatus);
|
116
|
-
}
|
117
|
-
});
|
118
|
-
}
|
132
|
+
},
|
133
|
+
}));
|
134
|
+
};
|
119
135
|
|
120
|
-
const isValueEmpty = function(value) {
|
121
|
-
return
|
122
|
-
|
136
|
+
const isValueEmpty = function (value) {
|
137
|
+
return (
|
138
|
+
value !== undefined &&
|
139
|
+
value !== null &&
|
140
|
+
value !== "" &&
|
141
|
+
(!Array.isArray(value) || value.length !== 0)
|
142
|
+
);
|
143
|
+
};
|
123
144
|
|
124
|
-
$(document).on(
|
125
|
-
var filterType = $(this).data(
|
126
|
-
var filterColumn = $(this).data(
|
145
|
+
$(document).on("change", '[data-behaviour="filter"]', function (e) {
|
146
|
+
var filterType = $(this).data("filter-type");
|
147
|
+
var filterColumn = $(this).data("db-column");
|
127
148
|
|
128
|
-
if (filterType ==
|
129
|
-
var rangeElements = $(this).parent().children()
|
130
|
-
var filterValue =
|
149
|
+
if (filterType == "range") {
|
150
|
+
var rangeElements = $(this).parent().children();
|
151
|
+
var filterValue =
|
152
|
+
$(rangeElements[0]).val() + " to " + $(rangeElements[1]).val();
|
131
153
|
} else {
|
132
|
-
var filterValue = $(this).val()
|
154
|
+
var filterValue = $(this).val();
|
133
155
|
}
|
134
156
|
|
135
|
-
$(this)
|
136
|
-
|
157
|
+
$(this)
|
158
|
+
.parents(":nth(1)")
|
159
|
+
.children(":first")
|
160
|
+
.children(":nth(1)")
|
161
|
+
.text(filterValue);
|
162
|
+
$(this)
|
163
|
+
.parents(":nth(1)")
|
164
|
+
.children(":first")
|
165
|
+
.children(":last")
|
166
|
+
.removeClass("hidden");
|
137
167
|
|
138
|
-
unhideClearFilterBtn(filterValue)
|
139
|
-
getFilteredData(filterType, filterValue, filterColumn)
|
168
|
+
unhideClearFilterBtn(filterValue);
|
169
|
+
getFilteredData(filterType, filterValue, filterColumn);
|
140
170
|
});
|
141
171
|
|
142
|
-
$(document).on(
|
172
|
+
$(document).on("keyup", '[data-behaviour="input-search"]', function (e) {
|
143
173
|
e.stopPropagation();
|
144
174
|
|
145
175
|
var searchValue = $(this).val();
|
146
|
-
unhideClearFilterBtn(searchValue)
|
147
|
-
getFilteredData(
|
176
|
+
unhideClearFilterBtn(searchValue);
|
177
|
+
getFilteredData("search", searchValue);
|
148
178
|
});
|
149
179
|
|
150
|
-
$(document).on(
|
151
|
-
var filterType = $(this).data(
|
152
|
-
var filterColumn = $(this).data(
|
180
|
+
$(document).on("click", '[data-behaviour="filter-option"]', function (e) {
|
181
|
+
var filterType = $(this).data("filter-type");
|
182
|
+
var filterColumn = $(this).data("db-column");
|
153
183
|
|
154
184
|
// Clear the search value post selection and regenerate the dropdown elements.
|
155
|
-
var searchInputElement = $(this)
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
185
|
+
var searchInputElement = $(this)
|
186
|
+
.parents(":nth(1)")
|
187
|
+
.children(":first")
|
188
|
+
.children();
|
189
|
+
searchInputElement.val("");
|
190
|
+
CmFilter.dropdown_search(searchInputElement);
|
191
|
+
|
192
|
+
unhideFilter(filterType, filterColumn);
|
160
193
|
});
|
161
194
|
|
162
|
-
var unhideFilter = function(filterType, filterColumn) {
|
163
|
-
var filterInputElement = $(
|
164
|
-
|
165
|
-
|
166
|
-
|
195
|
+
var unhideFilter = function (filterType, filterColumn) {
|
196
|
+
var filterInputElement = $(
|
197
|
+
'[data-behaviour="filter-input"][data-filter-type=' +
|
198
|
+
filterType +
|
199
|
+
"][data-db-column=" +
|
200
|
+
filterColumn +
|
201
|
+
"]"
|
202
|
+
);
|
203
|
+
|
204
|
+
filterInputElement.parent().removeClass("hidden");
|
205
|
+
filterInputElement.click();
|
167
206
|
};
|
168
207
|
|
169
208
|
// Search inside the dropdowns
|
170
|
-
$(document).on(
|
171
|
-
|
172
|
-
|
209
|
+
$(document).on(
|
210
|
+
"keyup",
|
211
|
+
'[data-behaviour="dropdown-filter-search"]',
|
212
|
+
function (e) {
|
213
|
+
CmFilter.dropdown_search($(this));
|
214
|
+
}
|
215
|
+
);
|
173
216
|
|
174
217
|
// Method to decode the encoded nested and/or complex hash and convert it to
|
175
218
|
// object that is used for filters while sending the data to the backend.
|
176
219
|
var getParamsAsObject = function (query) {
|
177
|
-
query = query.substring(query.indexOf(
|
220
|
+
query = query.substring(query.indexOf("?") + 1);
|
178
221
|
|
179
222
|
var re = /([^&=]+)=?([^&]*)/g;
|
180
223
|
var decodeRE = /\+/g;
|
@@ -183,199 +226,257 @@ var getParamsAsObject = function (query) {
|
|
183
226
|
return decodeURIComponent(str.replace(decodeRE, " "));
|
184
227
|
};
|
185
228
|
|
186
|
-
var params = {},
|
187
|
-
|
188
|
-
|
189
|
-
|
229
|
+
var params = {},
|
230
|
+
e;
|
231
|
+
while ((e = re.exec(query))) {
|
232
|
+
var k = decode(e[1]),
|
233
|
+
v = decode(e[2]);
|
234
|
+
if (k.substring(k.length - 2) === "[]") {
|
190
235
|
k = k.substring(0, k.length - 2);
|
191
236
|
(params[k] || (params[k] = [])).push(v);
|
192
|
-
}
|
193
|
-
else params[k] = v;
|
237
|
+
} else params[k] = v;
|
194
238
|
}
|
195
239
|
|
196
240
|
var assign = function (obj, keyPath, value) {
|
197
241
|
var lastKeyIndex = keyPath.length - 1;
|
198
242
|
for (var i = 0; i < lastKeyIndex; ++i) {
|
199
243
|
var key = keyPath[i];
|
200
|
-
if (!(key in obj))
|
201
|
-
obj[key] = {}
|
244
|
+
if (!(key in obj)) obj[key] = {};
|
202
245
|
obj = obj[key];
|
203
246
|
}
|
204
247
|
obj[keyPath[lastKeyIndex]] = value;
|
205
|
-
}
|
248
|
+
};
|
206
249
|
|
207
250
|
for (var prop in params) {
|
208
|
-
var structure = prop.split(
|
251
|
+
var structure = prop.split("[");
|
209
252
|
if (structure.length > 1) {
|
210
253
|
var levels = [];
|
211
254
|
structure.forEach(function (item, i) {
|
212
|
-
var key = item.replace(/[?[\]\\ ]/g,
|
255
|
+
var key = item.replace(/[?[\]\\ ]/g, "");
|
213
256
|
levels.push(key);
|
214
257
|
});
|
215
258
|
assign(params, levels, params[prop]);
|
216
|
-
delete
|
259
|
+
delete params[prop];
|
217
260
|
}
|
218
261
|
}
|
219
262
|
return params;
|
220
263
|
};
|
221
264
|
|
222
|
-
$(document).on(
|
223
|
-
var filterType = $(this).data(
|
224
|
-
var filterColumn = $(this).data(
|
225
|
-
|
226
|
-
var filterElement = $(
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
265
|
+
$(document).on("click", '[data-behaviour="filter-input"]', function (e) {
|
266
|
+
var filterType = $(this).data("filter-type");
|
267
|
+
var filterColumn = $(this).data("db-column");
|
268
|
+
|
269
|
+
var filterElement = $(
|
270
|
+
'[data-behaviour="filter"][data-filter-type=' +
|
271
|
+
filterType +
|
272
|
+
"][data-db-column=" +
|
273
|
+
filterColumn +
|
274
|
+
"]"
|
275
|
+
);
|
276
|
+
|
277
|
+
if (filterType == "range") {
|
278
|
+
filterElement.parent().toggleClass("hidden");
|
279
|
+
} else if (filterType == "date") {
|
280
|
+
filterElement.click();
|
232
281
|
}
|
233
|
-
})
|
282
|
+
});
|
234
283
|
|
235
284
|
// Remove all the applied filters and reload the page
|
236
|
-
$(document).on(
|
237
|
-
window.location.href = window.location.href.split("?")[0]
|
238
|
-
})
|
285
|
+
$(document).on("click", ".clear-btn", function (e) {
|
286
|
+
window.location.href = window.location.href.split("?")[0];
|
287
|
+
});
|
239
288
|
|
240
|
-
var unhideClearFilterBtn = function(filterValue) {
|
289
|
+
var unhideClearFilterBtn = function (filterValue) {
|
241
290
|
if (isValueEmpty(filterValue)) {
|
242
|
-
$(
|
291
|
+
$(".clear-btn").removeClass("hidden");
|
243
292
|
}
|
244
|
-
}
|
293
|
+
};
|
245
294
|
|
246
|
-
$(document).on(
|
247
|
-
|
248
|
-
|
295
|
+
$(document).on(
|
296
|
+
"click",
|
297
|
+
'[data-behaviour="select-option"] [data-behaviour="multi-select-filter-checkbox"]',
|
298
|
+
function (e) {
|
299
|
+
$(this).prop("checked", !$(this).prop("checked"));
|
300
|
+
}
|
301
|
+
);
|
249
302
|
// Selecting options for single and multi select filters
|
250
|
-
$(document).on(
|
251
|
-
var filterType = $(this).data(
|
252
|
-
var filterColumn = $(this).data(
|
253
|
-
|
254
|
-
if (filterType ==
|
255
|
-
var filterValue = $(this).data(
|
256
|
-
var filterText = $(this).text()
|
257
|
-
if (!this.classList.contains(
|
258
|
-
if (this.parentNode.querySelector(
|
259
|
-
this.parentNode
|
303
|
+
$(document).on("click", '[data-behaviour="select-option"]', function (e) {
|
304
|
+
var filterType = $(this).data("filter-type");
|
305
|
+
var filterColumn = $(this).data("db-column");
|
306
|
+
|
307
|
+
if (filterType == "single_select") {
|
308
|
+
var filterValue = $(this).data("value");
|
309
|
+
var filterText = $(this).text();
|
310
|
+
if (!this.classList.contains("selected")) {
|
311
|
+
if (this.parentNode.querySelector(".list-item.selected") != null) {
|
312
|
+
this.parentNode
|
313
|
+
.querySelector(".list-item.selected")
|
314
|
+
.classList.remove("selected");
|
260
315
|
}
|
261
|
-
$(this).addClass(
|
316
|
+
$(this).addClass("selected");
|
262
317
|
}
|
263
318
|
|
264
|
-
$(this)
|
265
|
-
|
319
|
+
$(this)
|
320
|
+
.parents(":nth(4)")
|
321
|
+
.children(":first")
|
322
|
+
.children(":nth(1)")
|
323
|
+
.text(filterText);
|
324
|
+
$(this)
|
325
|
+
.parents(":nth(4)")
|
326
|
+
.children(":first")
|
327
|
+
.children(":last")
|
328
|
+
.removeClass("hidden");
|
266
329
|
|
267
330
|
// Clear the search value post selection and regenerate the dropdown elements.
|
268
|
-
var searchInputElement = $(this)
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
var
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
331
|
+
var searchInputElement = $(this)
|
332
|
+
.parents(":nth(1)")
|
333
|
+
.children(":first")
|
334
|
+
.children();
|
335
|
+
searchInputElement.val("");
|
336
|
+
CmFilter.dropdown_search(searchInputElement);
|
337
|
+
|
338
|
+
unhideClearFilterBtn(filterValue);
|
339
|
+
getFilteredData(filterType, filterValue, filterColumn);
|
340
|
+
} else if (filterType == "multi_select") {
|
341
|
+
var parentChip = $(this).parent().siblings(":first");
|
342
|
+
var checkboxElement = $(this).find(
|
343
|
+
'[data-behaviour="multi-select-filter-checkbox"]'
|
344
|
+
);
|
345
|
+
checkboxElement.prop("checked", !checkboxElement.prop("checked"));
|
346
|
+
var checkedCount = $(this)
|
347
|
+
.parent()
|
348
|
+
.find('[data-behaviour="multi-select-filter-checkbox"]')
|
349
|
+
.filter(":checked").length;
|
350
|
+
var filterValue = [];
|
351
|
+
var filterValueText = [];
|
352
|
+
if (checkboxElement.prop("checked")) {
|
353
|
+
var chip = $('<div class="chip"></div>');
|
354
|
+
var firstSpan = $("<span></span>").text($(this).text());
|
355
|
+
var secondSpan = $(
|
356
|
+
'<span data-behaviour="selected-chip"><i class="fa fa-times"></i></span>'
|
357
|
+
);
|
358
|
+
parentChip.prepend(chip.append(firstSpan).append(secondSpan));
|
287
359
|
} else {
|
288
|
-
var chipElement = parentChip.find(
|
289
|
-
for(var i = 0; i < chipElement.length; i++) {
|
360
|
+
var chipElement = parentChip.find(".chip");
|
361
|
+
for (var i = 0; i < chipElement.length; i++) {
|
290
362
|
if ($(chipElement[i]).text() == $(this).text()) {
|
291
|
-
$(chipElement[i]).remove()
|
292
|
-
break
|
363
|
+
$(chipElement[i]).remove();
|
364
|
+
break;
|
293
365
|
}
|
294
366
|
}
|
295
367
|
}
|
296
|
-
$(this)
|
297
|
-
|
298
|
-
|
299
|
-
|
368
|
+
$(this)
|
369
|
+
.parent()
|
370
|
+
.find('[data-behaviour="multi-select-filter-checkbox"]')
|
371
|
+
.filter(":checked")
|
372
|
+
.each(function () {
|
373
|
+
filterValue.push($(this).parent().data("value"));
|
374
|
+
filterValueText.push($(this).parent().text());
|
375
|
+
});
|
300
376
|
|
301
377
|
if (checkedCount > 0) {
|
302
|
-
parentChip.addClass(
|
303
|
-
$(this).parents(
|
378
|
+
parentChip.addClass("active");
|
379
|
+
$(this).parents(":nth(1)").children(":last").addClass("active");
|
304
380
|
} else {
|
305
|
-
parentChip.removeClass(
|
306
|
-
$(this).parents(
|
381
|
+
parentChip.removeClass("active");
|
382
|
+
$(this).parents(":nth(1)").children(":last").removeClass("active");
|
307
383
|
}
|
308
384
|
// Truncate filter value logic
|
309
|
-
var truncatedFilterValue = filterValueText[0]
|
385
|
+
var truncatedFilterValue = filterValueText[0];
|
310
386
|
if (filterValue.length > 1) {
|
311
|
-
truncatedFilterValue +=
|
387
|
+
truncatedFilterValue += " + " + (filterValue.length - 1);
|
312
388
|
}
|
313
389
|
// Update the filter input element with the truncated value
|
314
|
-
var filterInputElement = $(this).parents(
|
390
|
+
var filterInputElement = $(this).parents(":nth(4)").children(":first");
|
315
391
|
if (filterValue.length > 0) {
|
316
|
-
filterInputElement.children(
|
317
|
-
filterInputElement.children(
|
392
|
+
filterInputElement.children(":nth(1)").text(truncatedFilterValue);
|
393
|
+
filterInputElement.children(":last").removeClass("hidden");
|
318
394
|
} else {
|
319
|
-
filterInputElement.children(
|
320
|
-
filterInputElement.children(
|
395
|
+
filterInputElement.children(":nth(1)").text("");
|
396
|
+
filterInputElement.children(":last").addClass("hidden");
|
321
397
|
}
|
322
398
|
// Clear the search value post selection and regenerate the dropdown elements.
|
323
|
-
var searchInputElement = $(this)
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
399
|
+
var searchInputElement = $(this)
|
400
|
+
.parents(":nth(1)")
|
401
|
+
.children(":first")
|
402
|
+
.children(":last");
|
403
|
+
searchInputElement.val("");
|
404
|
+
CmFilter.dropdown_search(searchInputElement);
|
405
|
+
unhideClearFilterBtn(filterValue);
|
406
|
+
getFilteredData(filterType, filterValue, filterColumn);
|
328
407
|
}
|
329
408
|
});
|
330
409
|
|
331
|
-
|
332
410
|
// Remove single applied filter.
|
333
|
-
$(document).on(
|
334
|
-
var url = window.location.pathname
|
335
|
-
var filterType = $(this).parent().data(
|
336
|
-
var filterColumn = $(this).parent().data(
|
411
|
+
$(document).on("click", ".filter-chip-remove", function (e) {
|
412
|
+
var url = window.location.pathname;
|
413
|
+
var filterType = $(this).parent().data("filter-type");
|
414
|
+
var filterColumn = $(this).parent().data("db-column");
|
337
415
|
|
338
|
-
var searchParams = window.location.search
|
416
|
+
var searchParams = window.location.search;
|
339
417
|
if (searchParams.length > 0) {
|
340
|
-
var queryString = getParamsAsObject(searchParams)
|
341
|
-
if (queryString[
|
342
|
-
delete
|
343
|
-
var queryParam = jQuery.param(queryString)
|
344
|
-
window.history.pushState("", "", url +
|
345
|
-
window.location.reload()
|
418
|
+
var queryString = getParamsAsObject(searchParams);
|
419
|
+
if (queryString["filters"][filterType] != undefined) {
|
420
|
+
delete queryString["filters"][filterType][filterColumn];
|
421
|
+
var queryParam = jQuery.param(queryString);
|
422
|
+
window.history.pushState("", "", url + "?" + queryParam);
|
423
|
+
window.location.reload();
|
346
424
|
}
|
347
425
|
}
|
348
426
|
});
|
349
427
|
|
350
|
-
$(document).on(
|
351
|
-
var filterType = $(this)
|
352
|
-
|
353
|
-
|
428
|
+
$(document).on("click", '[data-behaviour="selected-chip"]', function (e) {
|
429
|
+
var filterType = $(this)
|
430
|
+
.parents(":nth(5)")
|
431
|
+
.find(".filter-chip")
|
432
|
+
.data("filter-type");
|
433
|
+
var filterColumn = $(this)
|
434
|
+
.parents(":nth(5)")
|
435
|
+
.find(".filter-chip")
|
436
|
+
.data("db-column");
|
437
|
+
var filterValue = [];
|
354
438
|
var filterValueText = $(this).siblings().text();
|
355
|
-
var selectElement = $(
|
439
|
+
var selectElement = $(
|
440
|
+
'[data-behaviour="select-option"][data-filter-type=' +
|
441
|
+
filterType +
|
442
|
+
"][data-db-column=" +
|
443
|
+
filterColumn +
|
444
|
+
"]"
|
445
|
+
);
|
356
446
|
$(this).parent().remove();
|
357
447
|
for (var i = 0; i < selectElement.length; i++) {
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
448
|
+
if ($(selectElement[i]).text() == filterValueText) {
|
449
|
+
var checkboxElement = $(selectElement[i]).find(
|
450
|
+
'[data-behaviour="multi-select-filter-checkbox"]'
|
451
|
+
);
|
452
|
+
checkboxElement.prop("checked", false); // Uncheck the box
|
453
|
+
break;
|
454
|
+
}
|
363
455
|
}
|
364
|
-
selectElement
|
365
|
-
|
366
|
-
|
367
|
-
|
456
|
+
selectElement
|
457
|
+
.find('[data-behaviour="multi-select-filter-checkbox"]')
|
458
|
+
.filter(":checked")
|
459
|
+
.each(function () {
|
460
|
+
filterValue.push($(this).parent().data("value"));
|
461
|
+
});
|
462
|
+
var checkedCount = selectElement
|
463
|
+
.find('[data-behaviour="multi-select-filter-checkbox"]')
|
464
|
+
.filter(":checked").length;
|
368
465
|
if (checkedCount < 1) {
|
369
|
-
|
370
|
-
|
466
|
+
selectElement.parent().siblings(":first").removeClass("active");
|
467
|
+
selectElement.parent().siblings(":last").removeClass("active");
|
371
468
|
}
|
372
469
|
unhideClearFilterBtn(filterValue);
|
373
470
|
getFilteredData(filterType, filterValue, filterColumn);
|
374
471
|
});
|
375
472
|
|
376
|
-
$(document).on(
|
377
|
-
|
378
|
-
|
473
|
+
$(document).on(
|
474
|
+
"change",
|
475
|
+
'[data-behaviour="sort-column"], [data-behaviour="sort-direction"]',
|
476
|
+
function (e) {
|
477
|
+
getFilteredData(null, null, null, true);
|
478
|
+
}
|
479
|
+
);
|
379
480
|
|
380
481
|
$(document).on("click", '[data-behaviour="reset-sort"]', function (e) {
|
381
482
|
let url = window.location.pathname;
|
@@ -384,15 +485,15 @@ $(document).on("click", '[data-behaviour="reset-sort"]', function (e) {
|
|
384
485
|
delete queryString["sort_direction"];
|
385
486
|
let queryParam = jQuery.param(queryString);
|
386
487
|
$.ajax({
|
387
|
-
type:
|
488
|
+
type: "POST",
|
388
489
|
url: url + "/reset_sort_columns",
|
389
|
-
success: function(data) {
|
490
|
+
success: function (data) {
|
390
491
|
window.history.pushState("", "", url + "?" + queryParam);
|
391
492
|
window.location.reload();
|
392
493
|
},
|
393
|
-
error: function(jqxhr, textStatus, errorThrown) {
|
494
|
+
error: function (jqxhr, textStatus, errorThrown) {
|
394
495
|
console.log(errorThrown, textStatus);
|
395
|
-
}
|
496
|
+
},
|
396
497
|
});
|
397
498
|
});
|
398
499
|
|
@@ -401,8 +502,13 @@ $(document).on(
|
|
401
502
|
'[data-behaviour="multi-select-filter-apply"]',
|
402
503
|
function (e) {
|
403
504
|
$(this).parent();
|
404
|
-
$(this)
|
405
|
-
|
505
|
+
$(this)
|
506
|
+
.closest('[data-behaviour="multi-select-filter-dropdown"]')
|
507
|
+
.removeClass("show");
|
508
|
+
$(this)
|
509
|
+
.closest('[data-behaviour="multi-select-filter-wrapper"]')
|
510
|
+
.find('[data-behaviour="filter-input"]')
|
511
|
+
.removeClass("show");
|
406
512
|
}
|
407
513
|
);
|
408
514
|
|
@@ -410,56 +516,62 @@ $(document).on(
|
|
410
516
|
"click",
|
411
517
|
'[data-behaviour="multi-select-filter-clear"]',
|
412
518
|
function (e) {
|
413
|
-
const dropdown = $(this).closest(
|
519
|
+
const dropdown = $(this).closest(
|
520
|
+
'[data-behaviour="multi-select-filter-dropdown"]'
|
521
|
+
);
|
414
522
|
|
415
|
-
const filterInputElement = $(this).parents(
|
416
|
-
filterInputElement.children(
|
523
|
+
const filterInputElement = $(this).parents(":nth(4)").children(":first");
|
524
|
+
filterInputElement.children(":nth(1)").text("");
|
417
525
|
|
418
|
-
const checkboxes = dropdown.find(
|
419
|
-
|
526
|
+
const checkboxes = dropdown.find(
|
527
|
+
'[data-behaviour="multi-select-filter-checkbox"]:checked'
|
528
|
+
);
|
529
|
+
checkboxes.each(function () {
|
420
530
|
this.checked = false;
|
421
531
|
});
|
422
532
|
|
423
|
-
const chips = dropdown.find(
|
424
|
-
chips.each(function() {
|
533
|
+
const chips = dropdown.find(".chip");
|
534
|
+
chips.each(function () {
|
425
535
|
$(this).remove();
|
426
536
|
});
|
427
537
|
|
428
|
-
const chipsArea = dropdown.find(
|
429
|
-
|
538
|
+
const chipsArea = dropdown.find(
|
539
|
+
'[data-behaviour="multi-select-filter-chips-area"]'
|
540
|
+
);
|
541
|
+
chipsArea.removeClass("active");
|
430
542
|
|
431
543
|
const filter_element = dropdown.prev();
|
432
|
-
const db_column = filter_element.attr(
|
433
|
-
getFilteredData(filter_element.attr(
|
544
|
+
const db_column = filter_element.attr("data-db-column");
|
545
|
+
getFilteredData(filter_element.attr("data-filter-type"), [], db_column);
|
434
546
|
}
|
435
547
|
);
|
436
548
|
|
437
|
-
const initializeFilterLabels = function(filterParams) {
|
438
|
-
Object.keys(filterParams).forEach(filterType => {
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
549
|
+
const initializeFilterLabels = function (filterParams) {
|
550
|
+
Object.keys(filterParams).forEach((filterType) => {
|
551
|
+
const filterObj = filterParams[filterType];
|
552
|
+
Object.keys(filterObj).forEach((filterColumn) => {
|
553
|
+
const filterChip = $(`[data-db-column="${filterColumn}"]`);
|
554
|
+
const filterName = filterChip.find(".filter-name");
|
555
|
+
|
556
|
+
// Append "is" to indicate active filter
|
557
|
+
if (filterName.length && !filterName.text().endsWith("is")) {
|
558
|
+
filterName.html(`${filterName.text()} is `);
|
559
|
+
filterName.data("appended", true);
|
560
|
+
}
|
561
|
+
});
|
450
562
|
});
|
451
563
|
};
|
452
564
|
|
453
|
-
$(document).ready(function() {
|
565
|
+
$(document).ready(function () {
|
454
566
|
const initialFilterParams = getParamsAsObject(window.location.search);
|
455
567
|
initializeFilterLabels(initialFilterParams.filters || {});
|
456
568
|
});
|
457
569
|
|
458
|
-
$(document).ready(function() {
|
459
|
-
const chipsAreas = document.querySelectorAll(
|
460
|
-
|
461
|
-
|
570
|
+
$(document).ready(function () {
|
571
|
+
const chipsAreas = document.querySelectorAll(
|
572
|
+
'[data-behaviour="multi-select-filter-chips-area"]'
|
573
|
+
);
|
574
|
+
chipsAreas.forEach(function (chipsArea) {
|
575
|
+
chipsArea.classList.add("active");
|
462
576
|
});
|
463
577
|
});
|
464
|
-
|
465
|
-
|
@@ -1,5 +1,6 @@
|
|
1
1
|
// This file is shared between rails 6 and 7 version
|
2
2
|
import LocalTime from "./local-time";
|
3
|
+
|
3
4
|
$(document).on(
|
4
5
|
"keypress keyup blur",
|
5
6
|
"[data-behaviour='decimal-only'], [data-behaviour='filter'][data-filter-type='range']",
|
@@ -55,6 +56,11 @@ $(document).on("click", '[data-behaviour="offcanvas"]', function (e) {
|
|
55
56
|
);
|
56
57
|
drawerForm.show();
|
57
58
|
initializeComponents();
|
59
|
+
$("#cm-drawer .select-2").select2({
|
60
|
+
theme: "bootstrap-5",
|
61
|
+
dropdownParent: "#cm-drawer",
|
62
|
+
});
|
63
|
+
|
58
64
|
handleDrawerFormSubmission(drawerForm);
|
59
65
|
},
|
60
66
|
error: function (error) {
|
@@ -246,4 +252,4 @@ export function initializeComponents() {
|
|
246
252
|
var calculatedHeight = "calc(100vh - " + headerElemHeight + "px" + ")";
|
247
253
|
$(".table-wrapper").css("maxHeight", calculatedHeight);
|
248
254
|
LocalTime.start();
|
249
|
-
|
255
|
+
}
|
@@ -77,8 +77,7 @@
|
|
77
77
|
&:focus {
|
78
78
|
border-color: $brand-color !important;
|
79
79
|
outline: 0 !important;
|
80
|
-
box-shadow:
|
81
|
-
inset 0 1px 1px rgba(0, 0, 0, 0.075),
|
80
|
+
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075),
|
82
81
|
0 0 8px rgba(102, 175, 233, 0.6) !important;
|
83
82
|
}
|
84
83
|
}
|
@@ -96,8 +95,7 @@
|
|
96
95
|
&:focus {
|
97
96
|
border-color: $brand-color;
|
98
97
|
outline: 0;
|
99
|
-
box-shadow:
|
100
|
-
inset 0 1px 1px rgba(0, 0, 0, 0.075),
|
98
|
+
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075),
|
101
99
|
0 0 8px rgba(102, 175, 233, 0.6);
|
102
100
|
}
|
103
101
|
.select2-selection__rendered {
|
@@ -2,11 +2,14 @@ module CmAdmin
|
|
2
2
|
class StaticController < ::ActionController::Base
|
3
3
|
layout 'static'
|
4
4
|
|
5
|
-
def dashboard
|
6
|
-
end
|
5
|
+
def dashboard; end
|
7
6
|
|
8
|
-
def
|
9
|
-
|
7
|
+
def index
|
8
|
+
fallback_path = current_user.present? ? "#{CmAdmin::Engine.mount_path}/users" : '/users/sign_in'
|
9
|
+
redirect_path = current_user&.cm_role&.default_redirect_path || fallback_path
|
10
|
+
redirect_to redirect_path
|
10
11
|
end
|
12
|
+
|
13
|
+
def error_403; end
|
11
14
|
end
|
12
15
|
end
|
data/app/models/cm_role.rb
CHANGED
@@ -4,6 +4,19 @@ class CmRole < ApplicationRecord
|
|
4
4
|
has_many :cm_permissions
|
5
5
|
|
6
6
|
validates :name, uniqueness: true, presence: true
|
7
|
+
validate :valid_default_redirect_path
|
7
8
|
|
8
9
|
has_paper_trail
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def valid_default_redirect_path
|
14
|
+
return if default_redirect_path.blank?
|
15
|
+
|
16
|
+
begin
|
17
|
+
Rails.application.routes.recognize_path(default_redirect_path)
|
18
|
+
rescue ActionController::RoutingError
|
19
|
+
errors.add(:default_redirect_path, 'should be valid rails path')
|
20
|
+
end
|
21
|
+
end
|
9
22
|
end
|
@@ -18,7 +18,6 @@ module CmAdmin::CmRole
|
|
18
18
|
cm_show page_title: :name do
|
19
19
|
custom_action name: 'create_role_permission', route_type: 'member', verb: 'post', path: ':id/create_role_permission',
|
20
20
|
display_type: :route do
|
21
|
-
# allowed_params = params.permit(role_permission: []).to_h
|
22
21
|
@role = CmRole.find(params[:id])
|
23
22
|
params[:role_permission].except(:submit).each do |model_name, action_arr|
|
24
23
|
action_names = action_arr.select { |k, v| k if v.key?('is_checked') }.keys
|
@@ -37,6 +36,7 @@ module CmAdmin::CmRole
|
|
37
36
|
tab :profile, '' do
|
38
37
|
cm_show_section 'Role details' do
|
39
38
|
field :name
|
39
|
+
field :default_redirect_path
|
40
40
|
field :created_at, field_type: :date, format: '%d %b, %Y'
|
41
41
|
field :updated_at, field_type: :date, format: '%d %b, %Y'
|
42
42
|
end
|
@@ -47,12 +47,14 @@ module CmAdmin::CmRole
|
|
47
47
|
cm_new page_title: 'Add Role', page_description: 'Enter all details to add role' do
|
48
48
|
cm_section 'Details' do
|
49
49
|
form_field :name, input_type: :string
|
50
|
+
form_field :default_redirect_path, input_type: :string, helper_text: 'Enter a correct path to have default redirect path for this role. Usually it will be /cm_admin/<model name in plural>. Example: /cm_admin/users'
|
50
51
|
end
|
51
52
|
end
|
52
53
|
|
53
54
|
cm_edit page_title: 'Edit Role', page_description: 'Enter all details to edit role' do
|
54
55
|
cm_section 'Details' do
|
55
56
|
form_field :name, input_type: :string
|
57
|
+
form_field :default_redirect_path, input_type: :string, helper_text: 'Enter a correct path to have default redirect path for this role. Usually it will be /cm_admin/<model name in plural>. Example: /cm_admin/users'
|
56
58
|
end
|
57
59
|
end
|
58
60
|
end
|
@@ -55,4 +55,5 @@
|
|
55
55
|
p.count-text.m-0 Showing #{@ar_object.pagy.from} to #{@ar_object.pagy.to} out of #{@ar_object.pagy.count}
|
56
56
|
== render partial: 'cm_admin/main/cm_pagy_nav', locals: { pagy: @ar_object.pagy }
|
57
57
|
|
58
|
-
|
58
|
+
= column_pop_up(@model)
|
59
|
+
= manage_column_pop_up(@model)
|
@@ -11,4 +11,7 @@
|
|
11
11
|
|
12
12
|
.pagination-bar.kanban-pagination
|
13
13
|
.btn.btn-primary.kanban-show-more data-page=1 Show more
|
14
|
-
= render partial: 'cm_admin/main/show_as_drawer'
|
14
|
+
= render partial: 'cm_admin/main/show_as_drawer'
|
15
|
+
|
16
|
+
= column_pop_up(@model)
|
17
|
+
= manage_column_pop_up(@model)
|
@@ -52,3 +52,6 @@
|
|
52
52
|
.pagination-bar
|
53
53
|
p.count-text.m-0 Showing #{@ar_object.pagy.from} to #{@ar_object.pagy.to} out of #{@ar_object.pagy.count}
|
54
54
|
== render partial: 'cm_admin/main/cm_pagy_nav', locals: { pagy: @ar_object.pagy }
|
55
|
+
|
56
|
+
= column_pop_up(@model)
|
57
|
+
= manage_column_pop_up(@model)
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# Custom Filter Method
|
2
|
+
|
3
|
+
## Overview
|
4
|
+
|
5
|
+
We can add a custom filter method to filter records based on specific criteria. This is useful when the default filtering options do not meet the requirements. The custom filter method can be defined using the `filter_with` option.
|
6
|
+
|
7
|
+
## Usage
|
8
|
+
|
9
|
+
The custom filter method is implemented using the `filter` dsl_method with the `filter_with` option. Below are the key elements and their usage:
|
10
|
+
|
11
|
+
### Syntax
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
filter :filter_name, :filter_type, filter_with: :custom_filter_method
|
15
|
+
```
|
16
|
+
|
17
|
+
### Example
|
18
|
+
|
19
|
+
**1. For Search and Select:**
|
20
|
+
|
21
|
+
Define the custom filter method in your model:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
class YourModel < ApplicationRecord
|
25
|
+
scope :custom_filter_method, ->(value) { where('column_name LIKE ?', "%#{value}%") }
|
26
|
+
end
|
27
|
+
```
|
28
|
+
|
29
|
+
Use the custom filter method in your cm admin:
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
filter :filter_name, :search, filter_with: :custom_filter_method
|
33
|
+
```
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
filter :filter_name, :single_select, helper_method: :options_for_select, filter_with: :custom_filter_method
|
37
|
+
```
|
38
|
+
|
39
|
+
**2. For Date and Range:**
|
40
|
+
|
41
|
+
Define the custom filter method in your model:
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
class YourModel < ApplicationRecord
|
45
|
+
scope :custom_filter_method, ->(from, to) { where('column_name BETWEEN ? AND ?', from, to) }
|
46
|
+
end
|
47
|
+
```
|
48
|
+
|
49
|
+
Use the custom filter method in your cm admin:
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
filter :filter_name, :date, filter_with: :custom_filter_method
|
53
|
+
```
|
data/docs/RoleManagement.md
CHANGED
@@ -48,25 +48,25 @@ module Authentication
|
|
48
48
|
before_action :check_current_user
|
49
49
|
before_action :set_params
|
50
50
|
end
|
51
|
-
|
51
|
+
|
52
52
|
def set_params
|
53
53
|
Current.request_params = params if params
|
54
54
|
end
|
55
55
|
|
56
|
-
# Add other methods here
|
56
|
+
# Add other methods here
|
57
57
|
|
58
58
|
end
|
59
59
|
```
|
60
60
|
|
61
|
-
3. Add `belongs_to :cm_role, optional: true` in the `User` model.
|
62
|
-
4. Include `CmRole` in the `config.included_models` section of `config/initializers/zcm_admin.rb`.
|
61
|
+
3. Add `belongs_to :cm_role, optional: true` in the `User` model.
|
62
|
+
4. Include `CmRole` in the `config.included_models` section of `config/initializers/zcm_admin.rb`.
|
63
63
|
5. Assign `cm_role_id` to `1` for any user in the `User` Model, and use that user to log in.
|
64
64
|
|
65
65
|
## Setting up scopes
|
66
66
|
|
67
67
|
By default, `Full Access` scopes is added to each permission item. To add additional scopes, use the following syntax:
|
68
68
|
|
69
|
-
|
69
|
+
````ruby
|
70
70
|
...
|
71
71
|
cm_admin do
|
72
72
|
actions only: []
|
@@ -91,7 +91,7 @@ cm_admin do
|
|
91
91
|
page_title 'User'
|
92
92
|
end
|
93
93
|
end
|
94
|
-
|
94
|
+
````
|
95
95
|
|
96
96
|
Then, create a policy file for the respective model, e.g., `app/policies/cm_admin/user_policy.rb`:
|
97
97
|
|
@@ -106,7 +106,6 @@ end
|
|
106
106
|
|
107
107
|
This structure helps ensure that your application's role and permission management is both flexible and secure.
|
108
108
|
|
109
|
-
|
110
109
|
## Permission based fields
|
111
110
|
|
112
111
|
We can apply permission logic to display a field on the interface. You can do this with the following syntax.
|
@@ -126,3 +125,19 @@ end
|
|
126
125
|
|
127
126
|
```
|
128
127
|
|
128
|
+
## Adding Default Redirect Path for a Role
|
129
|
+
|
130
|
+
- We can add default redirect path for a role via cm admin.
|
131
|
+
- need to add this on `routes.rb`.
|
132
|
+
|
133
|
+
```ruby
|
134
|
+
...
|
135
|
+
root 'cm_admin/static#index'
|
136
|
+
```
|
137
|
+
|
138
|
+
- Create a migration to add a column for default redirect path. (Will be automatically added in new apps)
|
139
|
+
|
140
|
+
```ruby
|
141
|
+
...
|
142
|
+
add_column :cm_roles, :default_redirect_path, :string, default: "#{CmAdmin::Engine.mount_path}/users"
|
143
|
+
```
|
data/lib/cm_admin/version.rb
CHANGED
@@ -84,7 +84,7 @@ module CmAdmin
|
|
84
84
|
end
|
85
85
|
|
86
86
|
def toast_message(message, toast_type)
|
87
|
-
tag.div class: '
|
87
|
+
tag.div class: 'cm-toast-container' do
|
88
88
|
tag.div class: "toast #{toast_type == 'alert' ? 'text-white bg-danger' : ''}", role: 'alert', 'aria-live': 'assertive', 'aria-atomic': 'true', data: { behaviour: 'toast' } do
|
89
89
|
tag.div class: 'd-flex' do
|
90
90
|
concat tag.div message.html_safe, class: 'toast-body'
|
@@ -6,7 +6,7 @@ module CmAdmin
|
|
6
6
|
source_root File.expand_path('templates', __dir__)
|
7
7
|
|
8
8
|
def create_migration
|
9
|
-
generate 'migration', 'CreateCmRole name:string'
|
9
|
+
generate 'migration', 'CreateCmRole name:string default_redirect_path:string'
|
10
10
|
generate 'migration', 'CreateCmPermission action_name:string action_display_name:string ar_model_name:string scope_name:string cm_role:references'
|
11
11
|
rake 'db:migrate'
|
12
12
|
puts "Created migration"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cm-admin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.
|
4
|
+
version: 3.0.16
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael
|
@@ -14,7 +14,7 @@ authors:
|
|
14
14
|
autorequire:
|
15
15
|
bindir: exe
|
16
16
|
cert_chain: []
|
17
|
-
date: 2024-11-
|
17
|
+
date: 2024-11-25 00:00:00.000000000 Z
|
18
18
|
dependencies:
|
19
19
|
- !ruby/object:Gem::Dependency
|
20
20
|
name: caxlsx_rails
|
@@ -426,6 +426,7 @@ files:
|
|
426
426
|
- config/webpack/test.js
|
427
427
|
- config/webpacker.yml
|
428
428
|
- docs/AddingAlert.md
|
429
|
+
- docs/CustomFilterMethod.md
|
429
430
|
- docs/ListingSelectTwoAjax.md
|
430
431
|
- docs/ListingSelectTwoItems.md
|
431
432
|
- docs/RoleManagement.md
|