cm-admin 3.0.14 → 3.0.15
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 +348 -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/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 +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a68921cd2290bc55a0fe8bad15e16cfdac64d9365756190a08c1e5ca45b35f57
|
4
|
+
data.tar.gz: 56639a3c550354da66a09cff791cd342fef7e276ca627ab76124c07b1c593ac8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3f11275fc5ddb6cd3793da121a5ea235b7f6ad0aba979edf3eb37d58800bf881ef55c43afd25e52cefc3b795b1feed6186c645f6a6fed6581cedc868a731e587
|
7
|
+
data.tar.gz: f99f2472c8682e4517210b86aa5ebda52e7134b4f2fcfca4aa3418fae7f42773644852cc1bfd6b1cb7a3392fb4907c05033c79f4c9587805d8428af80b592963
|
data/Gemfile.lock
CHANGED
@@ -2,37 +2,45 @@ var currentRequest = null;
|
|
2
2
|
|
3
3
|
var CmFilter = {
|
4
4
|
// Generate or remove elements of the dropdown based on the search value.
|
5
|
-
dropdown_search: function(element) {
|
6
|
-
if(element.val()) {
|
5
|
+
dropdown_search: function (element) {
|
6
|
+
if (element.val()) {
|
7
7
|
var filter = element.val().toUpperCase();
|
8
|
-
var dropdownElements = element
|
8
|
+
var dropdownElements = element
|
9
|
+
.parents(":nth(1)")
|
10
|
+
.find(".list-area")
|
11
|
+
.children();
|
9
12
|
for (var i = 0; i < dropdownElements.length; i++) {
|
10
13
|
var txtValue = $(dropdownElements[i]).children().text();
|
11
14
|
if (txtValue.toUpperCase().indexOf(filter) > -1) {
|
12
|
-
$(dropdownElements[i]).css(
|
15
|
+
$(dropdownElements[i]).css("display", "flex");
|
13
16
|
} else {
|
14
|
-
$(dropdownElements[i]).css(
|
17
|
+
$(dropdownElements[i]).css("display", "none");
|
15
18
|
}
|
16
19
|
}
|
17
20
|
}
|
18
|
-
}
|
19
|
-
}
|
21
|
+
},
|
22
|
+
};
|
20
23
|
|
21
24
|
// Main method which will structure the existing filter values with the newly
|
22
25
|
// applied filter. Send and receive the value from the backend.
|
23
|
-
var getFilteredData = function(
|
26
|
+
var getFilteredData = function (
|
27
|
+
filterType,
|
28
|
+
filterValue,
|
29
|
+
filterColumn = null,
|
30
|
+
sortData = null
|
31
|
+
) {
|
24
32
|
let sortColumn = $('[data-behaviour="sort-column"]').val();
|
25
33
|
let sortDirection = $('[data-behaviour="sort-direction"]:checked').val();
|
26
34
|
|
27
|
-
var url = window.location.pathname
|
35
|
+
var url = window.location.pathname;
|
28
36
|
|
29
37
|
// Based on the value changed for recent filter generate the filterParams hash
|
30
38
|
var filterParams = {};
|
31
39
|
if (filterColumn) {
|
32
40
|
filterParams[filterType] = {};
|
33
|
-
filterParams[filterType][filterColumn] = filterValue
|
41
|
+
filterParams[filterType][filterColumn] = filterValue;
|
34
42
|
} else {
|
35
|
-
if (filterType) filterParams[filterType] = filterValue
|
43
|
+
if (filterType) filterParams[filterType] = filterValue;
|
36
44
|
}
|
37
45
|
|
38
46
|
// page params is reinitialized to 1 when any new filter value is applied so
|
@@ -42,139 +50,171 @@ var getFilteredData = function(filterType, filterValue, filterColumn=null, sortD
|
|
42
50
|
filters: filterParams,
|
43
51
|
sort_column: sortColumn,
|
44
52
|
sort_direction: sortDirection,
|
45
|
-
page: 1
|
53
|
+
page: 1,
|
46
54
|
};
|
47
55
|
|
48
56
|
// Generate the queryString by concatenating the filterParams and
|
49
57
|
// searchParams that are already applied, if searchParams are present.
|
50
|
-
var searchParams = window.location.search
|
51
|
-
var searchParamsHash = getParamsAsObject(searchParams)
|
58
|
+
var searchParams = window.location.search;
|
59
|
+
var searchParamsHash = getParamsAsObject(searchParams);
|
52
60
|
if (Object.keys(searchParamsHash).length > 0) {
|
53
61
|
// Delete the previous applied value for multi_select filter from the
|
54
62
|
// searchParams as altering the array with new and old value will create
|
55
63
|
// more complicated logic. The new value is passed and structured in
|
56
64
|
// filterParams and will be concadinated with the searchParams post deletion
|
57
|
-
if (filterType ==
|
58
|
-
searchParams = getParamsAsObject(searchParams)
|
59
|
-
if (
|
60
|
-
|
65
|
+
if (filterType == "multi_select") {
|
66
|
+
searchParams = getParamsAsObject(searchParams);
|
67
|
+
if (
|
68
|
+
searchParams["filters"] &&
|
69
|
+
searchParams["filters"][filterType] !== undefined
|
70
|
+
) {
|
71
|
+
delete searchParams["filters"][filterType][filterColumn];
|
61
72
|
}
|
62
|
-
searchParams = jQuery.param(searchParams)
|
73
|
+
searchParams = jQuery.param(searchParams);
|
63
74
|
}
|
64
|
-
filterParams = jQuery.param(queryString)
|
65
|
-
var availableParams = searchParams +
|
66
|
-
var queryString = getParamsAsObject(availableParams)
|
75
|
+
filterParams = jQuery.param(queryString);
|
76
|
+
var availableParams = searchParams + "&" + filterParams;
|
77
|
+
var queryString = getParamsAsObject(availableParams);
|
67
78
|
}
|
68
|
-
|
79
|
+
|
69
80
|
if (filterColumn) {
|
70
81
|
const filterChip = $(`[data-db-column="${filterColumn}"]`);
|
71
|
-
const filterName = filterChip.find(
|
72
|
-
|
82
|
+
const filterName = filterChip.find(".filter-name");
|
83
|
+
|
73
84
|
if (isValueEmpty(filterValue)) {
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
85
|
+
if (!filterName.data("appended")) {
|
86
|
+
filterName.html(`${filterName.text()} is `);
|
87
|
+
filterName.data("appended", true);
|
88
|
+
}
|
78
89
|
} else {
|
79
|
-
|
80
|
-
|
90
|
+
filterName.html(filterName.text().replace(/\s*is $/, ""));
|
91
|
+
filterName.data("appended", false);
|
81
92
|
}
|
82
93
|
}
|
83
94
|
|
84
|
-
return currentRequest = $.ajax(url, {
|
85
|
-
type:
|
95
|
+
return (currentRequest = $.ajax(url, {
|
96
|
+
type: "GET",
|
86
97
|
data: queryString,
|
87
|
-
beforeSend: function() {
|
98
|
+
beforeSend: function () {
|
88
99
|
if (currentRequest !== null) {
|
89
100
|
currentRequest.abort();
|
90
101
|
}
|
91
102
|
},
|
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
|
-
$(
|
103
|
+
success: function (data) {
|
104
|
+
var queryParam = jQuery.param(queryString);
|
105
|
+
window.history.pushState("", "", url + "?" + queryParam);
|
106
|
+
if ($("#view_type").val() == "kanban") {
|
107
|
+
$.each(data.table.data, function (key, value) {
|
108
|
+
$("." + key + " .cards").html(value);
|
98
109
|
});
|
99
|
-
$(
|
100
|
-
$.each(data.table.column_count, function(key, value) {
|
101
|
-
$(
|
110
|
+
$(".kanban-list .counter").html(0);
|
111
|
+
$.each(data.table.column_count, function (key, value) {
|
112
|
+
$("." + key + " .counter").html(value);
|
102
113
|
});
|
103
114
|
} else {
|
104
|
-
$(
|
115
|
+
$(".cm-index-page__table-container").html(data);
|
105
116
|
}
|
106
117
|
if (sortData) {
|
107
|
-
const dropdownElement = document.querySelector(
|
118
|
+
const dropdownElement = document.querySelector(
|
119
|
+
'[data-behaviour="sort-button-toggle"]'
|
120
|
+
);
|
108
121
|
if (dropdownElement) {
|
109
122
|
const dropdown = new bootstrap.Dropdown(dropdownElement);
|
110
123
|
dropdown.show();
|
111
124
|
}
|
112
125
|
}
|
113
126
|
},
|
114
|
-
error: function(jqxhr, textStatus, errorThrown) {
|
127
|
+
error: function (jqxhr, textStatus, errorThrown) {
|
115
128
|
console.log(errorThrown, textStatus);
|
116
|
-
}
|
117
|
-
});
|
118
|
-
}
|
129
|
+
},
|
130
|
+
}));
|
131
|
+
};
|
119
132
|
|
120
|
-
const isValueEmpty = function(value) {
|
121
|
-
return
|
122
|
-
|
133
|
+
const isValueEmpty = function (value) {
|
134
|
+
return (
|
135
|
+
value !== undefined &&
|
136
|
+
value !== null &&
|
137
|
+
value !== "" &&
|
138
|
+
(!Array.isArray(value) || value.length !== 0)
|
139
|
+
);
|
140
|
+
};
|
123
141
|
|
124
|
-
$(document).on(
|
125
|
-
var filterType = $(this).data(
|
126
|
-
var filterColumn = $(this).data(
|
142
|
+
$(document).on("change", '[data-behaviour="filter"]', function (e) {
|
143
|
+
var filterType = $(this).data("filter-type");
|
144
|
+
var filterColumn = $(this).data("db-column");
|
127
145
|
|
128
|
-
if (filterType ==
|
129
|
-
var rangeElements = $(this).parent().children()
|
130
|
-
var filterValue =
|
146
|
+
if (filterType == "range") {
|
147
|
+
var rangeElements = $(this).parent().children();
|
148
|
+
var filterValue =
|
149
|
+
$(rangeElements[0]).val() + " to " + $(rangeElements[1]).val();
|
131
150
|
} else {
|
132
|
-
var filterValue = $(this).val()
|
151
|
+
var filterValue = $(this).val();
|
133
152
|
}
|
134
153
|
|
135
|
-
$(this)
|
136
|
-
|
154
|
+
$(this)
|
155
|
+
.parents(":nth(1)")
|
156
|
+
.children(":first")
|
157
|
+
.children(":nth(1)")
|
158
|
+
.text(filterValue);
|
159
|
+
$(this)
|
160
|
+
.parents(":nth(1)")
|
161
|
+
.children(":first")
|
162
|
+
.children(":last")
|
163
|
+
.removeClass("hidden");
|
137
164
|
|
138
|
-
unhideClearFilterBtn(filterValue)
|
139
|
-
getFilteredData(filterType, filterValue, filterColumn)
|
165
|
+
unhideClearFilterBtn(filterValue);
|
166
|
+
getFilteredData(filterType, filterValue, filterColumn);
|
140
167
|
});
|
141
168
|
|
142
|
-
$(document).on(
|
169
|
+
$(document).on("keyup", '[data-behaviour="input-search"]', function (e) {
|
143
170
|
e.stopPropagation();
|
144
171
|
|
145
172
|
var searchValue = $(this).val();
|
146
|
-
unhideClearFilterBtn(searchValue)
|
147
|
-
getFilteredData(
|
173
|
+
unhideClearFilterBtn(searchValue);
|
174
|
+
getFilteredData("search", searchValue);
|
148
175
|
});
|
149
176
|
|
150
|
-
$(document).on(
|
151
|
-
var filterType = $(this).data(
|
152
|
-
var filterColumn = $(this).data(
|
177
|
+
$(document).on("click", '[data-behaviour="filter-option"]', function (e) {
|
178
|
+
var filterType = $(this).data("filter-type");
|
179
|
+
var filterColumn = $(this).data("db-column");
|
153
180
|
|
154
181
|
// Clear the search value post selection and regenerate the dropdown elements.
|
155
|
-
var searchInputElement = $(this)
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
182
|
+
var searchInputElement = $(this)
|
183
|
+
.parents(":nth(1)")
|
184
|
+
.children(":first")
|
185
|
+
.children();
|
186
|
+
searchInputElement.val("");
|
187
|
+
CmFilter.dropdown_search(searchInputElement);
|
188
|
+
|
189
|
+
unhideFilter(filterType, filterColumn);
|
160
190
|
});
|
161
191
|
|
162
|
-
var unhideFilter = function(filterType, filterColumn) {
|
163
|
-
var filterInputElement = $(
|
164
|
-
|
165
|
-
|
166
|
-
|
192
|
+
var unhideFilter = function (filterType, filterColumn) {
|
193
|
+
var filterInputElement = $(
|
194
|
+
'[data-behaviour="filter-input"][data-filter-type=' +
|
195
|
+
filterType +
|
196
|
+
"][data-db-column=" +
|
197
|
+
filterColumn +
|
198
|
+
"]"
|
199
|
+
);
|
200
|
+
|
201
|
+
filterInputElement.parent().removeClass("hidden");
|
202
|
+
filterInputElement.click();
|
167
203
|
};
|
168
204
|
|
169
205
|
// Search inside the dropdowns
|
170
|
-
$(document).on(
|
171
|
-
|
172
|
-
|
206
|
+
$(document).on(
|
207
|
+
"keyup",
|
208
|
+
'[data-behaviour="dropdown-filter-search"]',
|
209
|
+
function (e) {
|
210
|
+
CmFilter.dropdown_search($(this));
|
211
|
+
}
|
212
|
+
);
|
173
213
|
|
174
214
|
// Method to decode the encoded nested and/or complex hash and convert it to
|
175
215
|
// object that is used for filters while sending the data to the backend.
|
176
216
|
var getParamsAsObject = function (query) {
|
177
|
-
query = query.substring(query.indexOf(
|
217
|
+
query = query.substring(query.indexOf("?") + 1);
|
178
218
|
|
179
219
|
var re = /([^&=]+)=?([^&]*)/g;
|
180
220
|
var decodeRE = /\+/g;
|
@@ -183,199 +223,257 @@ var getParamsAsObject = function (query) {
|
|
183
223
|
return decodeURIComponent(str.replace(decodeRE, " "));
|
184
224
|
};
|
185
225
|
|
186
|
-
var params = {},
|
187
|
-
|
188
|
-
|
189
|
-
|
226
|
+
var params = {},
|
227
|
+
e;
|
228
|
+
while ((e = re.exec(query))) {
|
229
|
+
var k = decode(e[1]),
|
230
|
+
v = decode(e[2]);
|
231
|
+
if (k.substring(k.length - 2) === "[]") {
|
190
232
|
k = k.substring(0, k.length - 2);
|
191
233
|
(params[k] || (params[k] = [])).push(v);
|
192
|
-
}
|
193
|
-
else params[k] = v;
|
234
|
+
} else params[k] = v;
|
194
235
|
}
|
195
236
|
|
196
237
|
var assign = function (obj, keyPath, value) {
|
197
238
|
var lastKeyIndex = keyPath.length - 1;
|
198
239
|
for (var i = 0; i < lastKeyIndex; ++i) {
|
199
240
|
var key = keyPath[i];
|
200
|
-
if (!(key in obj))
|
201
|
-
obj[key] = {}
|
241
|
+
if (!(key in obj)) obj[key] = {};
|
202
242
|
obj = obj[key];
|
203
243
|
}
|
204
244
|
obj[keyPath[lastKeyIndex]] = value;
|
205
|
-
}
|
245
|
+
};
|
206
246
|
|
207
247
|
for (var prop in params) {
|
208
|
-
var structure = prop.split(
|
248
|
+
var structure = prop.split("[");
|
209
249
|
if (structure.length > 1) {
|
210
250
|
var levels = [];
|
211
251
|
structure.forEach(function (item, i) {
|
212
|
-
var key = item.replace(/[?[\]\\ ]/g,
|
252
|
+
var key = item.replace(/[?[\]\\ ]/g, "");
|
213
253
|
levels.push(key);
|
214
254
|
});
|
215
255
|
assign(params, levels, params[prop]);
|
216
|
-
delete
|
256
|
+
delete params[prop];
|
217
257
|
}
|
218
258
|
}
|
219
259
|
return params;
|
220
260
|
};
|
221
261
|
|
222
|
-
$(document).on(
|
223
|
-
var filterType = $(this).data(
|
224
|
-
var filterColumn = $(this).data(
|
225
|
-
|
226
|
-
var filterElement = $(
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
262
|
+
$(document).on("click", '[data-behaviour="filter-input"]', function (e) {
|
263
|
+
var filterType = $(this).data("filter-type");
|
264
|
+
var filterColumn = $(this).data("db-column");
|
265
|
+
|
266
|
+
var filterElement = $(
|
267
|
+
'[data-behaviour="filter"][data-filter-type=' +
|
268
|
+
filterType +
|
269
|
+
"][data-db-column=" +
|
270
|
+
filterColumn +
|
271
|
+
"]"
|
272
|
+
);
|
273
|
+
|
274
|
+
if (filterType == "range") {
|
275
|
+
filterElement.parent().toggleClass("hidden");
|
276
|
+
} else if (filterType == "date") {
|
277
|
+
filterElement.click();
|
232
278
|
}
|
233
|
-
})
|
279
|
+
});
|
234
280
|
|
235
281
|
// Remove all the applied filters and reload the page
|
236
|
-
$(document).on(
|
237
|
-
window.location.href = window.location.href.split("?")[0]
|
238
|
-
})
|
282
|
+
$(document).on("click", ".clear-btn", function (e) {
|
283
|
+
window.location.href = window.location.href.split("?")[0];
|
284
|
+
});
|
239
285
|
|
240
|
-
var unhideClearFilterBtn = function(filterValue) {
|
286
|
+
var unhideClearFilterBtn = function (filterValue) {
|
241
287
|
if (isValueEmpty(filterValue)) {
|
242
|
-
$(
|
288
|
+
$(".clear-btn").removeClass("hidden");
|
243
289
|
}
|
244
|
-
}
|
290
|
+
};
|
245
291
|
|
246
|
-
$(document).on(
|
247
|
-
|
248
|
-
|
292
|
+
$(document).on(
|
293
|
+
"click",
|
294
|
+
'[data-behaviour="select-option"] [data-behaviour="multi-select-filter-checkbox"]',
|
295
|
+
function (e) {
|
296
|
+
$(this).prop("checked", !$(this).prop("checked"));
|
297
|
+
}
|
298
|
+
);
|
249
299
|
// 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
|
300
|
+
$(document).on("click", '[data-behaviour="select-option"]', function (e) {
|
301
|
+
var filterType = $(this).data("filter-type");
|
302
|
+
var filterColumn = $(this).data("db-column");
|
303
|
+
|
304
|
+
if (filterType == "single_select") {
|
305
|
+
var filterValue = $(this).data("value");
|
306
|
+
var filterText = $(this).text();
|
307
|
+
if (!this.classList.contains("selected")) {
|
308
|
+
if (this.parentNode.querySelector(".list-item.selected") != null) {
|
309
|
+
this.parentNode
|
310
|
+
.querySelector(".list-item.selected")
|
311
|
+
.classList.remove("selected");
|
260
312
|
}
|
261
|
-
$(this).addClass(
|
313
|
+
$(this).addClass("selected");
|
262
314
|
}
|
263
315
|
|
264
|
-
$(this)
|
265
|
-
|
316
|
+
$(this)
|
317
|
+
.parents(":nth(4)")
|
318
|
+
.children(":first")
|
319
|
+
.children(":nth(1)")
|
320
|
+
.text(filterText);
|
321
|
+
$(this)
|
322
|
+
.parents(":nth(4)")
|
323
|
+
.children(":first")
|
324
|
+
.children(":last")
|
325
|
+
.removeClass("hidden");
|
266
326
|
|
267
327
|
// 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
|
-
|
328
|
+
var searchInputElement = $(this)
|
329
|
+
.parents(":nth(1)")
|
330
|
+
.children(":first")
|
331
|
+
.children();
|
332
|
+
searchInputElement.val("");
|
333
|
+
CmFilter.dropdown_search(searchInputElement);
|
334
|
+
|
335
|
+
unhideClearFilterBtn(filterValue);
|
336
|
+
getFilteredData(filterType, filterValue, filterColumn);
|
337
|
+
} else if (filterType == "multi_select") {
|
338
|
+
var parentChip = $(this).parent().siblings(":first");
|
339
|
+
var checkboxElement = $(this).find(
|
340
|
+
'[data-behaviour="multi-select-filter-checkbox"]'
|
341
|
+
);
|
342
|
+
checkboxElement.prop("checked", !checkboxElement.prop("checked"));
|
343
|
+
var checkedCount = $(this)
|
344
|
+
.parent()
|
345
|
+
.find('[data-behaviour="multi-select-filter-checkbox"]')
|
346
|
+
.filter(":checked").length;
|
347
|
+
var filterValue = [];
|
348
|
+
var filterValueText = [];
|
349
|
+
if (checkboxElement.prop("checked")) {
|
350
|
+
var chip = $('<div class="chip"></div>');
|
351
|
+
var firstSpan = $("<span></span>").text($(this).text());
|
352
|
+
var secondSpan = $(
|
353
|
+
'<span data-behaviour="selected-chip"><i class="fa fa-times"></i></span>'
|
354
|
+
);
|
355
|
+
parentChip.prepend(chip.append(firstSpan).append(secondSpan));
|
287
356
|
} else {
|
288
|
-
var chipElement = parentChip.find(
|
289
|
-
for(var i = 0; i < chipElement.length; i++) {
|
357
|
+
var chipElement = parentChip.find(".chip");
|
358
|
+
for (var i = 0; i < chipElement.length; i++) {
|
290
359
|
if ($(chipElement[i]).text() == $(this).text()) {
|
291
|
-
$(chipElement[i]).remove()
|
292
|
-
break
|
360
|
+
$(chipElement[i]).remove();
|
361
|
+
break;
|
293
362
|
}
|
294
363
|
}
|
295
364
|
}
|
296
|
-
$(this)
|
297
|
-
|
298
|
-
|
299
|
-
|
365
|
+
$(this)
|
366
|
+
.parent()
|
367
|
+
.find('[data-behaviour="multi-select-filter-checkbox"]')
|
368
|
+
.filter(":checked")
|
369
|
+
.each(function () {
|
370
|
+
filterValue.push($(this).parent().data("value"));
|
371
|
+
filterValueText.push($(this).parent().text());
|
372
|
+
});
|
300
373
|
|
301
374
|
if (checkedCount > 0) {
|
302
|
-
parentChip.addClass(
|
303
|
-
$(this).parents(
|
375
|
+
parentChip.addClass("active");
|
376
|
+
$(this).parents(":nth(1)").children(":last").addClass("active");
|
304
377
|
} else {
|
305
|
-
parentChip.removeClass(
|
306
|
-
$(this).parents(
|
378
|
+
parentChip.removeClass("active");
|
379
|
+
$(this).parents(":nth(1)").children(":last").removeClass("active");
|
307
380
|
}
|
308
381
|
// Truncate filter value logic
|
309
|
-
var truncatedFilterValue = filterValueText[0]
|
382
|
+
var truncatedFilterValue = filterValueText[0];
|
310
383
|
if (filterValue.length > 1) {
|
311
|
-
truncatedFilterValue +=
|
384
|
+
truncatedFilterValue += " + " + (filterValue.length - 1);
|
312
385
|
}
|
313
386
|
// Update the filter input element with the truncated value
|
314
|
-
var filterInputElement = $(this).parents(
|
387
|
+
var filterInputElement = $(this).parents(":nth(4)").children(":first");
|
315
388
|
if (filterValue.length > 0) {
|
316
|
-
filterInputElement.children(
|
317
|
-
filterInputElement.children(
|
389
|
+
filterInputElement.children(":nth(1)").text(truncatedFilterValue);
|
390
|
+
filterInputElement.children(":last").removeClass("hidden");
|
318
391
|
} else {
|
319
|
-
filterInputElement.children(
|
320
|
-
filterInputElement.children(
|
392
|
+
filterInputElement.children(":nth(1)").text("");
|
393
|
+
filterInputElement.children(":last").addClass("hidden");
|
321
394
|
}
|
322
395
|
// Clear the search value post selection and regenerate the dropdown elements.
|
323
|
-
var searchInputElement = $(this)
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
396
|
+
var searchInputElement = $(this)
|
397
|
+
.parents(":nth(1)")
|
398
|
+
.children(":first")
|
399
|
+
.children(":last");
|
400
|
+
searchInputElement.val("");
|
401
|
+
CmFilter.dropdown_search(searchInputElement);
|
402
|
+
unhideClearFilterBtn(filterValue);
|
403
|
+
getFilteredData(filterType, filterValue, filterColumn);
|
328
404
|
}
|
329
405
|
});
|
330
406
|
|
331
|
-
|
332
407
|
// 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(
|
408
|
+
$(document).on("click", ".filter-chip-remove", function (e) {
|
409
|
+
var url = window.location.pathname;
|
410
|
+
var filterType = $(this).parent().data("filter-type");
|
411
|
+
var filterColumn = $(this).parent().data("db-column");
|
337
412
|
|
338
|
-
var searchParams = window.location.search
|
413
|
+
var searchParams = window.location.search;
|
339
414
|
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()
|
415
|
+
var queryString = getParamsAsObject(searchParams);
|
416
|
+
if (queryString["filters"][filterType] != undefined) {
|
417
|
+
delete queryString["filters"][filterType][filterColumn];
|
418
|
+
var queryParam = jQuery.param(queryString);
|
419
|
+
window.history.pushState("", "", url + "?" + queryParam);
|
420
|
+
window.location.reload();
|
346
421
|
}
|
347
422
|
}
|
348
423
|
});
|
349
424
|
|
350
|
-
$(document).on(
|
351
|
-
var filterType = $(this)
|
352
|
-
|
353
|
-
|
425
|
+
$(document).on("click", '[data-behaviour="selected-chip"]', function (e) {
|
426
|
+
var filterType = $(this)
|
427
|
+
.parents(":nth(5)")
|
428
|
+
.find(".filter-chip")
|
429
|
+
.data("filter-type");
|
430
|
+
var filterColumn = $(this)
|
431
|
+
.parents(":nth(5)")
|
432
|
+
.find(".filter-chip")
|
433
|
+
.data("db-column");
|
434
|
+
var filterValue = [];
|
354
435
|
var filterValueText = $(this).siblings().text();
|
355
|
-
var selectElement = $(
|
436
|
+
var selectElement = $(
|
437
|
+
'[data-behaviour="select-option"][data-filter-type=' +
|
438
|
+
filterType +
|
439
|
+
"][data-db-column=" +
|
440
|
+
filterColumn +
|
441
|
+
"]"
|
442
|
+
);
|
356
443
|
$(this).parent().remove();
|
357
444
|
for (var i = 0; i < selectElement.length; i++) {
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
445
|
+
if ($(selectElement[i]).text() == filterValueText) {
|
446
|
+
var checkboxElement = $(selectElement[i]).find(
|
447
|
+
'[data-behaviour="multi-select-filter-checkbox"]'
|
448
|
+
);
|
449
|
+
checkboxElement.prop("checked", false); // Uncheck the box
|
450
|
+
break;
|
451
|
+
}
|
363
452
|
}
|
364
|
-
selectElement
|
365
|
-
|
366
|
-
|
367
|
-
|
453
|
+
selectElement
|
454
|
+
.find('[data-behaviour="multi-select-filter-checkbox"]')
|
455
|
+
.filter(":checked")
|
456
|
+
.each(function () {
|
457
|
+
filterValue.push($(this).parent().data("value"));
|
458
|
+
});
|
459
|
+
var checkedCount = selectElement
|
460
|
+
.find('[data-behaviour="multi-select-filter-checkbox"]')
|
461
|
+
.filter(":checked").length;
|
368
462
|
if (checkedCount < 1) {
|
369
|
-
|
370
|
-
|
463
|
+
selectElement.parent().siblings(":first").removeClass("active");
|
464
|
+
selectElement.parent().siblings(":last").removeClass("active");
|
371
465
|
}
|
372
466
|
unhideClearFilterBtn(filterValue);
|
373
467
|
getFilteredData(filterType, filterValue, filterColumn);
|
374
468
|
});
|
375
469
|
|
376
|
-
$(document).on(
|
377
|
-
|
378
|
-
|
470
|
+
$(document).on(
|
471
|
+
"change",
|
472
|
+
'[data-behaviour="sort-column"], [data-behaviour="sort-direction"]',
|
473
|
+
function (e) {
|
474
|
+
getFilteredData(null, null, null, true);
|
475
|
+
}
|
476
|
+
);
|
379
477
|
|
380
478
|
$(document).on("click", '[data-behaviour="reset-sort"]', function (e) {
|
381
479
|
let url = window.location.pathname;
|
@@ -384,15 +482,15 @@ $(document).on("click", '[data-behaviour="reset-sort"]', function (e) {
|
|
384
482
|
delete queryString["sort_direction"];
|
385
483
|
let queryParam = jQuery.param(queryString);
|
386
484
|
$.ajax({
|
387
|
-
type:
|
485
|
+
type: "POST",
|
388
486
|
url: url + "/reset_sort_columns",
|
389
|
-
success: function(data) {
|
487
|
+
success: function (data) {
|
390
488
|
window.history.pushState("", "", url + "?" + queryParam);
|
391
489
|
window.location.reload();
|
392
490
|
},
|
393
|
-
error: function(jqxhr, textStatus, errorThrown) {
|
491
|
+
error: function (jqxhr, textStatus, errorThrown) {
|
394
492
|
console.log(errorThrown, textStatus);
|
395
|
-
}
|
493
|
+
},
|
396
494
|
});
|
397
495
|
});
|
398
496
|
|
@@ -401,8 +499,13 @@ $(document).on(
|
|
401
499
|
'[data-behaviour="multi-select-filter-apply"]',
|
402
500
|
function (e) {
|
403
501
|
$(this).parent();
|
404
|
-
$(this)
|
405
|
-
|
502
|
+
$(this)
|
503
|
+
.closest('[data-behaviour="multi-select-filter-dropdown"]')
|
504
|
+
.removeClass("show");
|
505
|
+
$(this)
|
506
|
+
.closest('[data-behaviour="multi-select-filter-wrapper"]')
|
507
|
+
.find('[data-behaviour="filter-input"]')
|
508
|
+
.removeClass("show");
|
406
509
|
}
|
407
510
|
);
|
408
511
|
|
@@ -410,56 +513,62 @@ $(document).on(
|
|
410
513
|
"click",
|
411
514
|
'[data-behaviour="multi-select-filter-clear"]',
|
412
515
|
function (e) {
|
413
|
-
const dropdown = $(this).closest(
|
516
|
+
const dropdown = $(this).closest(
|
517
|
+
'[data-behaviour="multi-select-filter-dropdown"]'
|
518
|
+
);
|
414
519
|
|
415
|
-
const filterInputElement = $(this).parents(
|
416
|
-
filterInputElement.children(
|
520
|
+
const filterInputElement = $(this).parents(":nth(4)").children(":first");
|
521
|
+
filterInputElement.children(":nth(1)").text("");
|
417
522
|
|
418
|
-
const checkboxes = dropdown.find(
|
419
|
-
|
523
|
+
const checkboxes = dropdown.find(
|
524
|
+
'[data-behaviour="multi-select-filter-checkbox"]:checked'
|
525
|
+
);
|
526
|
+
checkboxes.each(function () {
|
420
527
|
this.checked = false;
|
421
528
|
});
|
422
529
|
|
423
|
-
const chips = dropdown.find(
|
424
|
-
chips.each(function() {
|
530
|
+
const chips = dropdown.find(".chip");
|
531
|
+
chips.each(function () {
|
425
532
|
$(this).remove();
|
426
533
|
});
|
427
534
|
|
428
|
-
const chipsArea = dropdown.find(
|
429
|
-
|
535
|
+
const chipsArea = dropdown.find(
|
536
|
+
'[data-behaviour="multi-select-filter-chips-area"]'
|
537
|
+
);
|
538
|
+
chipsArea.removeClass("active");
|
430
539
|
|
431
540
|
const filter_element = dropdown.prev();
|
432
|
-
const db_column = filter_element.attr(
|
433
|
-
getFilteredData(filter_element.attr(
|
541
|
+
const db_column = filter_element.attr("data-db-column");
|
542
|
+
getFilteredData(filter_element.attr("data-filter-type"), [], db_column);
|
434
543
|
}
|
435
544
|
);
|
436
545
|
|
437
|
-
const initializeFilterLabels = function(filterParams) {
|
438
|
-
Object.keys(filterParams).forEach(filterType => {
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
546
|
+
const initializeFilterLabels = function (filterParams) {
|
547
|
+
Object.keys(filterParams).forEach((filterType) => {
|
548
|
+
const filterObj = filterParams[filterType];
|
549
|
+
Object.keys(filterObj).forEach((filterColumn) => {
|
550
|
+
const filterChip = $(`[data-db-column="${filterColumn}"]`);
|
551
|
+
const filterName = filterChip.find(".filter-name");
|
552
|
+
|
553
|
+
// Append "is" to indicate active filter
|
554
|
+
if (filterName.length && !filterName.text().endsWith("is")) {
|
555
|
+
filterName.html(`${filterName.text()} is `);
|
556
|
+
filterName.data("appended", true);
|
557
|
+
}
|
558
|
+
});
|
450
559
|
});
|
451
560
|
};
|
452
561
|
|
453
|
-
$(document).ready(function() {
|
562
|
+
$(document).ready(function () {
|
454
563
|
const initialFilterParams = getParamsAsObject(window.location.search);
|
455
564
|
initializeFilterLabels(initialFilterParams.filters || {});
|
456
565
|
});
|
457
566
|
|
458
|
-
$(document).ready(function() {
|
459
|
-
const chipsAreas = document.querySelectorAll(
|
460
|
-
|
461
|
-
|
567
|
+
$(document).ready(function () {
|
568
|
+
const chipsAreas = document.querySelectorAll(
|
569
|
+
'[data-behaviour="multi-select-filter-chips-area"]'
|
570
|
+
);
|
571
|
+
chipsAreas.forEach(function (chipsArea) {
|
572
|
+
chipsArea.classList.add("active");
|
462
573
|
});
|
463
574
|
});
|
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)
|
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.15
|
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-18 00:00:00.000000000 Z
|
18
18
|
dependencies:
|
19
19
|
- !ruby/object:Gem::Dependency
|
20
20
|
name: caxlsx_rails
|