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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: df3fadb7ab461116a44e122cd8ec236b43841f501c898cfea6faa4ca898ef982
4
- data.tar.gz: 2c4bc421eebb4ebd67242ae920f4e2d29170606525a72fc57f1807a3aa557e66
3
+ metadata.gz: f3debddacbd593b7ae649770a5237ea0af2f7c3481cfdb75f93adf833345c702
4
+ data.tar.gz: f2d63c47f670a66b78241fa45398edfac023c36acc89d079065734319e72bccd
5
5
  SHA512:
6
- metadata.gz: e8cb857fe0f0d2b673223a9b364b73b0049206dbeaa109f954db6cd483fa6873bafe66000ee2393311d881efa2dcd9d901003fad5766918013044f9bf4a65680
7
- data.tar.gz: 98bac5325e98387bacb6fc03be9dd880c74410c2bfd48d20eaf90784014c2eaa23393544a2564de2fad95bf0bb6e9716cbdcc4a7b271d5c4d6c8abdd0ae44a2d
6
+ metadata.gz: 7d04f7523d8e25ef11d45025a4e8adbb3f583c4d1c5b8e353cd30a7f0d055a79280cfe081a1120721f83377476ba074334d9ec01e822595b372e1c78f2c64217
7
+ data.tar.gz: 76b3873cc8a0bea1c6ec2bd435538c9e06304310000a77d7380e2fc9bbb46232734a6d1881ccb566ca7aa135679606dea7b374777b292092f1747a2736cc8adc
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cm-admin (3.0.14)
4
+ cm-admin (3.0.16)
5
5
  caxlsx_rails
6
6
  cocoon (~> 1.2.15)
7
7
  csv-importer (~> 0.8.2)
@@ -1,7 +1,7 @@
1
1
  import * as bootstrap from "bootstrap";
2
2
  window.bootstrap = bootstrap;
3
3
 
4
- document.addEventListener("turbo:load", function () {
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.parents(':nth(1)').find('.list-area').children();
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('display', 'flex');
17
+ $(dropdownElements[i]).css("display", "flex");
13
18
  } else {
14
- $(dropdownElements[i]).css('display', 'none');
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(filterType, filterValue, filterColumn=null, sortData=null) {
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 == 'multi_select') {
58
- searchParams = getParamsAsObject(searchParams)
59
- if (searchParams['filters'] && searchParams['filters'][filterType] !== undefined) {
60
- delete(searchParams['filters'][filterType][filterColumn])
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 + '&' + filterParams
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('.filter-name');
72
-
84
+ const filterName = filterChip.find(".filter-name");
85
+
73
86
  if (isValueEmpty(filterValue)) {
74
- if (!filterName.data('appended')) {
75
- filterName.html(`${filterName.text()} is `);
76
- filterName.data('appended', true);
77
- }
87
+ if (!filterName.data("appended")) {
88
+ filterName.html(`${filterName.text()} is `);
89
+ filterName.data("appended", true);
90
+ }
78
91
  } else {
79
- filterName.html(filterName.text().replace(/\s*is $/, ''));
80
- filterName.data('appended', false);
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: 'GET',
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 + '?' + queryParam);
95
- if ($('#view_type').val() == 'kanban') {
96
- $.each(data.table.data, function(key, value) {
97
- $('.' + key + ' .cards').html(value)
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
- $('.kanban-list .counter').html(0)
100
- $.each(data.table.column_count, function(key, value) {
101
- $('.' + key + ' .counter').html(value)
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
- $('.cm-index-page__table-container').html(data);
117
+ $(".cm-index-page__table-container").html(data);
105
118
  }
106
119
  if (sortData) {
107
- const dropdownElement = document.querySelector('[data-behaviour="sort-button-toggle"]');
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 value !== undefined && value !== null && value !== '' && (!Array.isArray(value) || value.length !== 0);
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('change', '[data-behaviour="filter"]', function(e) {
125
- var filterType = $(this).data('filter-type')
126
- var filterColumn = $(this).data('db-column')
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 == 'range') {
129
- var rangeElements = $(this).parent().children()
130
- var filterValue = $(rangeElements[0]).val() + ' to ' + $(rangeElements[1]).val()
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).parents(':nth(1)').children(':first').children(':nth(1)').text(filterValue)
136
- $(this).parents(':nth(1)').children(':first').children(':last').removeClass('hidden')
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('keyup', '[data-behaviour="input-search"]', function(e) {
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('search', searchValue)
176
+ unhideClearFilterBtn(searchValue);
177
+ getFilteredData("search", searchValue);
148
178
  });
149
179
 
150
- $(document).on('click', '[data-behaviour="filter-option"]', function(e) {
151
- var filterType = $(this).data('filter-type')
152
- var filterColumn = $(this).data('db-column')
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).parents(':nth(1)').children(':first').children()
156
- searchInputElement.val('')
157
- CmFilter.dropdown_search(searchInputElement)
158
-
159
- unhideFilter(filterType, filterColumn)
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 = $('[data-behaviour="filter-input"][data-filter-type=' + filterType + '][data-db-column='+ filterColumn + ']')
164
-
165
- filterInputElement.parent().removeClass('hidden');
166
- filterInputElement.click()
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('keyup', '[data-behaviour="dropdown-filter-search"]', function(e) {
171
- CmFilter.dropdown_search($(this))
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('?') + 1);
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 = {}, e;
187
- while (e = re.exec(query)) {
188
- var k = decode(e[1]), v = decode(e[2]);
189
- if (k.substring(k.length - 2) === '[]') {
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(params[prop]);
259
+ delete params[prop];
217
260
  }
218
261
  }
219
262
  return params;
220
263
  };
221
264
 
222
- $(document).on('click', '[data-behaviour="filter-input"]', function(e) {
223
- var filterType = $(this).data('filter-type')
224
- var filterColumn = $(this).data('db-column')
225
-
226
- var filterElement = $('[data-behaviour="filter"][data-filter-type=' + filterType + '][data-db-column=' + filterColumn +']')
227
-
228
- if (filterType == 'range') {
229
- filterElement.parent().toggleClass('hidden')
230
- } else if (filterType == 'date') {
231
- filterElement.click()
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('click', '.clear-btn', function(e) {
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
- $('.clear-btn').removeClass('hidden')
291
+ $(".clear-btn").removeClass("hidden");
243
292
  }
244
- }
293
+ };
245
294
 
246
- $(document).on('click', '[data-behaviour="select-option"] [data-behaviour="multi-select-filter-checkbox"]', function(e) {
247
- $(this).prop('checked', !$(this).prop('checked'));
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('click', '[data-behaviour="select-option"]', function(e) {
251
- var filterType = $(this).data('filter-type')
252
- var filterColumn = $(this).data('db-column')
253
-
254
- if (filterType == 'single_select') {
255
- var filterValue = $(this).data('value')
256
- var filterText = $(this).text()
257
- if (!this.classList.contains('selected')) {
258
- if (this.parentNode.querySelector('.list-item.selected') != null) {
259
- this.parentNode.querySelector('.list-item.selected').classList.remove('selected');
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('selected')
316
+ $(this).addClass("selected");
262
317
  }
263
318
 
264
- $(this).parents(':nth(4)').children(':first').children(':nth(1)').text(filterText)
265
- $(this).parents(':nth(4)').children(':first').children(':last').removeClass('hidden')
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).parents(':nth(1)').children(':first').children()
269
- searchInputElement.val('')
270
- CmFilter.dropdown_search(searchInputElement)
271
-
272
- unhideClearFilterBtn(filterValue)
273
- getFilteredData(filterType, filterValue, filterColumn)
274
- }
275
- else if (filterType == 'multi_select') {
276
- var parentChip = $(this).parent().siblings(':first')
277
- var checkboxElement = $(this).find('[data-behaviour="multi-select-filter-checkbox"]')
278
- checkboxElement.prop('checked', !checkboxElement.prop('checked'))
279
- var checkedCount = $(this).parent().find('[data-behaviour="multi-select-filter-checkbox"]').filter(':checked').length
280
- var filterValue = []
281
- var filterValueText = []
282
- if (checkboxElement.prop('checked')) {
283
- var chip = $('<div class="chip"></div>')
284
- var firstSpan = $('<span></span>').text($(this).text())
285
- var secondSpan = $('<span data-behaviour="selected-chip"><i class="fa fa-times"></i></span>')
286
- parentChip.prepend(chip.append(firstSpan).append(secondSpan))
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('.chip')
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).parent().find('[data-behaviour="multi-select-filter-checkbox"]').filter(':checked').each(function() {
297
- filterValue.push($(this).parent().data('value'))
298
- filterValueText.push($(this).parent().text())
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('active')
303
- $(this).parents(':nth(1)').children(':last').addClass('active')
378
+ parentChip.addClass("active");
379
+ $(this).parents(":nth(1)").children(":last").addClass("active");
304
380
  } else {
305
- parentChip.removeClass('active')
306
- $(this).parents(':nth(1)').children(':last').removeClass('active')
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 += ' + ' + (filterValue.length - 1);
387
+ truncatedFilterValue += " + " + (filterValue.length - 1);
312
388
  }
313
389
  // Update the filter input element with the truncated value
314
- var filterInputElement = $(this).parents(':nth(4)').children(':first')
390
+ var filterInputElement = $(this).parents(":nth(4)").children(":first");
315
391
  if (filterValue.length > 0) {
316
- filterInputElement.children(':nth(1)').text(truncatedFilterValue)
317
- filterInputElement.children(':last').removeClass('hidden')
392
+ filterInputElement.children(":nth(1)").text(truncatedFilterValue);
393
+ filterInputElement.children(":last").removeClass("hidden");
318
394
  } else {
319
- filterInputElement.children(':nth(1)').text('')
320
- filterInputElement.children(':last').addClass('hidden')
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).parents(':nth(1)').children(':first').children(':last')
324
- searchInputElement.val('')
325
- CmFilter.dropdown_search(searchInputElement)
326
- unhideClearFilterBtn(filterValue)
327
- getFilteredData(filterType, filterValue, filterColumn)
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('click', '.filter-chip-remove', function(e) {
334
- var url = window.location.pathname
335
- var filterType = $(this).parent().data('filter-type')
336
- var filterColumn = $(this).parent().data('db-column')
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['filters'][filterType] != undefined) {
342
- delete(queryString['filters'][filterType][filterColumn])
343
- var queryParam = jQuery.param(queryString)
344
- window.history.pushState("", "", url + '?' + queryParam);
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('click', '[data-behaviour="selected-chip"]', function(e) {
351
- var filterType = $(this).parents(':nth(5)').find('.filter-chip').data('filter-type');
352
- var filterColumn = $(this).parents(':nth(5)').find('.filter-chip').data('db-column');
353
- var filterValue = [];
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 = $('[data-behaviour="select-option"][data-filter-type=' + filterType + '][data-db-column=' + filterColumn + ']');
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
- if ($(selectElement[i]).text() == filterValueText) {
359
- var checkboxElement = $(selectElement[i]).find('[data-behaviour="multi-select-filter-checkbox"]');
360
- checkboxElement.prop('checked', false); // Uncheck the box
361
- break;
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.find('[data-behaviour="multi-select-filter-checkbox"]').filter(':checked').each(function() {
365
- filterValue.push($(this).parent().data('value'));
366
- });
367
- var checkedCount = selectElement.find('[data-behaviour="multi-select-filter-checkbox"]').filter(':checked').length;
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
- selectElement.parent().siblings(':first').removeClass('active');
370
- selectElement.parent().siblings(':last').removeClass('active');
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("change", '[data-behaviour="sort-column"], [data-behaviour="sort-direction"]', function (e) {
377
- getFilteredData(null, null, null, true);
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: 'POST',
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).closest('[data-behaviour="multi-select-filter-dropdown"]').removeClass('show');
405
- $(this).closest('[data-behaviour="multi-select-filter-wrapper"]').find('[data-behaviour="filter-input"]').removeClass('show');
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('[data-behaviour="multi-select-filter-dropdown"]');
519
+ const dropdown = $(this).closest(
520
+ '[data-behaviour="multi-select-filter-dropdown"]'
521
+ );
414
522
 
415
- const filterInputElement = $(this).parents(':nth(4)').children(':first')
416
- filterInputElement.children(':nth(1)').text('')
523
+ const filterInputElement = $(this).parents(":nth(4)").children(":first");
524
+ filterInputElement.children(":nth(1)").text("");
417
525
 
418
- const checkboxes = dropdown.find('[data-behaviour="multi-select-filter-checkbox"]:checked');
419
- checkboxes.each(function() {
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('.chip');
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('[data-behaviour="multi-select-filter-chips-area"]');
429
- chipsArea.removeClass('active');
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('data-db-column');
433
- getFilteredData(filter_element.attr('data-filter-type'), [], db_column);
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
- const filterObj = filterParams[filterType];
440
- Object.keys(filterObj).forEach(filterColumn => {
441
- const filterChip = $(`[data-db-column="${filterColumn}"]`);
442
- const filterName = filterChip.find('.filter-name');
443
-
444
- // Append "is" to indicate active filter
445
- if (filterName.length && !filterName.text().endsWith('is')) {
446
- filterName.html(`${filterName.text()} is `);
447
- filterName.data('appended', true);
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('[data-behaviour="multi-select-filter-chips-area"]');
460
- chipsAreas.forEach(function(chipsArea) {
461
- chipsArea.classList.add('active');
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 {
@@ -132,3 +132,7 @@ a {
132
132
 
133
133
  z-index: 11;
134
134
  }
135
+
136
+ .toast {
137
+ background-color: $white;
138
+ }
@@ -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 error_403
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
@@ -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
- / = render partial: 'cm_admin/main/member_custom_action_modal', locals: { cm_model: @model, ar_collection: @ar_object }
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)
@@ -15,6 +15,3 @@
15
15
  == render "cm_admin/#{@model.name.underscore}/card"
16
16
  - elsif params[:view_type] == 'kanban' || @current_action.view_type == :kanban
17
17
  == render 'cm_admin/main/kanban'
18
- = column_pop_up(@model)
19
- = manage_column_pop_up(@model)
20
-
@@ -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
+ ```
@@ -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
- ```ruby
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
+ ```
@@ -1,3 +1,3 @@
1
1
  module CmAdmin
2
- VERSION = '3.0.14'
2
+ VERSION = '3.0.16'
3
3
  end
@@ -84,7 +84,7 @@ module CmAdmin
84
84
  end
85
85
 
86
86
  def toast_message(message, toast_type)
87
- tag.div class: 'position-fixed bottom-0 end-0 p-3', style: 'z-index: 11' do
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.14
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-08 00:00:00.000000000 Z
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