cm-admin 2.3.3 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/app/assets/javascripts/cm_admin/scaffolds.js +43 -28
- data/app/assets/javascripts/cm_admin/shared_scaffolds.js +109 -38
- data/app/assets/stylesheets/cm_admin/base/scaffold.scss +6 -0
- data/app/assets/stylesheets/cm_admin/cm_admin.css.scss +0 -1
- data/app/assets/stylesheets/cm_admin/components/_drawer.scss +17 -100
- data/app/assets/stylesheets/cm_admin/scaffold.scss +0 -14
- data/app/controllers/cm_admin/resource_controller.rb +45 -9
- data/app/javascript/stylesheets/cm_admin/application.scss +0 -1
- data/app/views/cm_admin/main/_associated_table.html.slim +7 -6
- data/app/views/cm_admin/main/_card.html.slim +0 -11
- data/app/views/cm_admin/main/_kanban.html.slim +0 -7
- data/app/views/cm_admin/main/_table.html.slim +6 -7
- data/app/views/cm_admin/main/edit.html.slim +1 -0
- data/app/views/cm_admin/main/new.html.slim +4 -3
- data/app/views/layouts/_cm_flash_message.html.slim +3 -6
- data/app/views/layouts/_drawer.html.slim +23 -0
- data/app/views/layouts/cm_admin.html.slim +1 -1
- data/config/importmap.rb +0 -1
- data/config/routes.rb +4 -0
- data/lib/cm_admin/model.rb +1 -1
- data/lib/cm_admin/models/action.rb +11 -7
- data/lib/cm_admin/models/form_field.rb +2 -1
- data/lib/cm_admin/version.rb +1 -1
- data/lib/cm_admin/view_helpers/filter_helper.rb +2 -2
- data/lib/cm_admin/view_helpers/form_helper.rb +37 -8
- data/lib/cm_admin/view_helpers/page_info_helper.rb +21 -16
- data/lib/cm_admin/view_helpers.rb +11 -0
- metadata +3 -4
- data/app/assets/stylesheets/cm_admin/dependency/jquery-jgrowl.min.css +0 -1
- data/app/views/cm_admin/main/_drawer.html.slim +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 78211818c7078c8cf9723ca166d19c9eaea3155544754a60fca317a8eaf86276
|
4
|
+
data.tar.gz: 7a53125a99db46cc8a7686076baf674eb971f20430bf680c5c0e105b06c89dc1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a2da45fd35ae854471c2c0255359fc96df661ebcb482c452a65734daf3364465bfe7cbb5250488a13be7381a7c129af90288b31d5c364c65bd878523ed8c75b8
|
7
|
+
data.tar.gz: a7ed71594d873f6c525577f2f19a48efe68bb873f331ab9b49bec2b18b980ea03bdd86fcbd8d0d83d2eb78bb3a3805e97626806c83ac43b138b88dc376638335
|
data/Gemfile.lock
CHANGED
@@ -15,7 +15,6 @@ import "moment";
|
|
15
15
|
import "bootstrap";
|
16
16
|
import "@popperjs/core";
|
17
17
|
import "flatpickr";
|
18
|
-
import "jgrowl";
|
19
18
|
import "trix";
|
20
19
|
import "@rails/actiontext";
|
21
20
|
import Select2 from "select2";
|
@@ -23,7 +22,6 @@ Select2();
|
|
23
22
|
|
24
23
|
// import '@nathanvda/cocoon'
|
25
24
|
import "daterangepicker";
|
26
|
-
import jqueryJgrowl from "jgrowl";
|
27
25
|
|
28
26
|
document.addEventListener("turbo:load", function () {
|
29
27
|
flatpickr("[data-behaviour='date-only']", {
|
@@ -38,19 +36,25 @@ document.addEventListener("turbo:load", function () {
|
|
38
36
|
$(".select-2").select2({
|
39
37
|
theme: "bootstrap-5",
|
40
38
|
});
|
41
|
-
jqueryJgrowl();
|
42
39
|
setup_select_2_ajax();
|
43
|
-
|
44
|
-
if(
|
45
|
-
|
46
|
-
|
40
|
+
const bsToast = $('[data-behaviour="toast"]')[0];
|
41
|
+
if (bsToast) {
|
42
|
+
const toast = new bootstrap.Toast(bsToast);
|
43
|
+
toast.show();
|
44
|
+
}
|
45
|
+
var el = $("[data-section='nested-form-body']");
|
46
|
+
if (el[0]) {
|
47
|
+
Sortable.create(el[0], {
|
48
|
+
handle: ".drag-handle",
|
47
49
|
animation: 150,
|
48
50
|
onUpdate: function (evt) {
|
49
|
-
var itemEl = evt.item
|
50
|
-
$("[data-section='nested-form-body'] tr").each(function(index, el){
|
51
|
-
$(el)
|
52
|
-
|
53
|
-
|
51
|
+
var itemEl = evt.item;
|
52
|
+
$("[data-section='nested-form-body'] tr").each(function (index, el) {
|
53
|
+
$(el)
|
54
|
+
.find(".hidden-position")
|
55
|
+
.val(index + 1);
|
56
|
+
});
|
57
|
+
},
|
54
58
|
});
|
55
59
|
}
|
56
60
|
});
|
@@ -84,18 +88,25 @@ $(document).on(
|
|
84
88
|
"click",
|
85
89
|
'[data-behaviour="permission-check-box"]',
|
86
90
|
function (e) {
|
87
|
-
var form_check = $(this).closest(
|
88
|
-
var form_field = $(this).closest(
|
89
|
-
if($(this).is(
|
90
|
-
form_check.find(
|
91
|
+
var form_check = $(this).closest(".form-check");
|
92
|
+
var form_field = $(this).closest(".form-field");
|
93
|
+
if ($(this).is(":checked")) {
|
94
|
+
form_check.find(".cm-select-tag").removeClass("hidden");
|
91
95
|
} else {
|
92
|
-
form_check.find(
|
96
|
+
form_check.find(".cm-select-tag").addClass("hidden");
|
93
97
|
}
|
94
|
-
if (
|
95
|
-
form_field.find('[data-behaviour="permission-check-
|
96
|
-
|
97
|
-
|
98
|
-
|
98
|
+
if (
|
99
|
+
form_field.find('[data-behaviour="permission-check-box"]:checked')
|
100
|
+
.length ==
|
101
|
+
form_field.find('[data-behaviour="permission-check-box"]').length
|
102
|
+
) {
|
103
|
+
form_field
|
104
|
+
.find('[data-behaviour="permission-check-all"]')
|
105
|
+
.prop("checked", true);
|
106
|
+
} else {
|
107
|
+
form_field
|
108
|
+
.find('[data-behaviour="permission-check-all"]')
|
109
|
+
.prop("checked", false);
|
99
110
|
}
|
100
111
|
}
|
101
112
|
);
|
@@ -104,13 +115,17 @@ $(document).on(
|
|
104
115
|
"click",
|
105
116
|
'[data-behaviour="permission-check-all"]',
|
106
117
|
function (e) {
|
107
|
-
var form_field = $(this).closest(
|
108
|
-
if($(this).is(
|
109
|
-
form_field
|
110
|
-
|
118
|
+
var form_field = $(this).closest(".form-field");
|
119
|
+
if ($(this).is(":checked")) {
|
120
|
+
form_field
|
121
|
+
.find('[data-behaviour="permission-check-box"]')
|
122
|
+
.prop("checked", true);
|
123
|
+
form_field.find(".cm-select-tag").removeClass("hidden");
|
111
124
|
} else {
|
112
|
-
form_field
|
113
|
-
|
125
|
+
form_field
|
126
|
+
.find('[data-behaviour="permission-check-box"]')
|
127
|
+
.prop("checked", false);
|
128
|
+
form_field.find(".cm-select-tag").addClass("hidden");
|
114
129
|
}
|
115
130
|
}
|
116
131
|
);
|
@@ -39,36 +39,107 @@ $(document).on("mouseleave", ".row-action-cell", function () {
|
|
39
39
|
$(this).find(".table-export-popup").addClass("hidden");
|
40
40
|
});
|
41
41
|
|
42
|
-
$(document).on("click",
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
42
|
+
$(document).on("click", '[data-behaviour="offcanvas"]', function (e) {
|
43
|
+
const drawerFetchUrl = $(this).attr("data-drawer-fetch-url");
|
44
|
+
const drawerContainer = $("[data-behaviour='cm-drawer-container']");
|
45
|
+
|
46
|
+
if (!drawerFetchUrl || !drawerContainer) return;
|
47
|
+
|
48
|
+
$.ajax({
|
49
|
+
url: drawerFetchUrl,
|
50
|
+
method: "GET",
|
51
|
+
success: function (response) {
|
52
|
+
drawerContainer.html(response);
|
53
|
+
const drawerForm = new bootstrap.Offcanvas(
|
54
|
+
drawerContainer.children().first()
|
55
|
+
);
|
56
|
+
drawerForm.show();
|
57
|
+
initializeComponents();
|
58
|
+
handleDrawerFormSubmission(drawerForm);
|
59
|
+
},
|
60
|
+
error: function (error) {
|
61
|
+
console.error("Error:", error);
|
62
|
+
},
|
63
|
+
});
|
55
64
|
});
|
56
65
|
|
57
|
-
$(document).on("click",
|
58
|
-
|
59
|
-
$(".drawer-container").removeClass("drawer-slide-in");
|
60
|
-
$(".drawer-container").addClass("drawer-slide-out");
|
61
|
-
setTimeout(() => {
|
62
|
-
$(".cm-drawer").addClass("hidden");
|
63
|
-
}, 300);
|
66
|
+
$(document).on("click", '[data-bs-dismiss="offcanvas"]', function (e) {
|
67
|
+
$(document).off("submit", "[data-is-drawer-form='true']");
|
64
68
|
});
|
65
69
|
|
70
|
+
function handleDrawerFormSubmission(drawerForm) {
|
71
|
+
$(document).on("submit", "[data-is-drawer-form='true']", function (e) {
|
72
|
+
e.preventDefault();
|
73
|
+
let url = $(this).attr("action");
|
74
|
+
if (url.charAt(url.length - 1) === "/") {
|
75
|
+
url = url.slice(0, -1);
|
76
|
+
}
|
77
|
+
url = `${url}.json`;
|
78
|
+
$.ajaxSetup({
|
79
|
+
headers: {
|
80
|
+
"X-CSRF-Token": $('meta[name="csrf-token"]').attr("content"),
|
81
|
+
},
|
82
|
+
});
|
83
|
+
const isMultipart = $(this).attr("enctype") === "multipart/form-data";
|
84
|
+
const ajaxOptions = {
|
85
|
+
url: url,
|
86
|
+
method: $(this).attr("method"),
|
87
|
+
data: null,
|
88
|
+
success: function (response) {
|
89
|
+
$('[data-behavior="flag-alert"]').addClass("hidden");
|
90
|
+
drawerForm.hide();
|
91
|
+
$(document).off("submit", "[data-is-drawer-form='true']");
|
92
|
+
const fromFieldId = $('[data-behavior="cm-drawer"]').attr(
|
93
|
+
"data-from-field-id"
|
94
|
+
);
|
95
|
+
const fromField = $(`#${fromFieldId}`);
|
96
|
+
toastMessage(response?.message);
|
97
|
+
const bsToast = $('[data-behaviour="drawer-toast"]')[0];
|
98
|
+
if (bsToast) {
|
99
|
+
const toast = new bootstrap.Toast(bsToast);
|
100
|
+
toast.show();
|
101
|
+
}
|
102
|
+
fromField.append(
|
103
|
+
`<option value="${response?.data?.id}">${response?.data?.name}</option>`
|
104
|
+
);
|
105
|
+
fromField.val(response?.data?.id).change();
|
106
|
+
},
|
107
|
+
error: function (error) {
|
108
|
+
$('[data-behavior="flag-alert"]').removeClass("hidden");
|
109
|
+
$('[data-behavior="error-list"]').html(error?.responseJSON?.message);
|
110
|
+
},
|
111
|
+
};
|
112
|
+
if (isMultipart) {
|
113
|
+
ajaxOptions["data"] = new FormData($(this)[0]);
|
114
|
+
ajaxOptions["contentType"] = false;
|
115
|
+
ajaxOptions["processData"] = false;
|
116
|
+
$.ajax(ajaxOptions);
|
117
|
+
} else {
|
118
|
+
ajaxOptions["data"] = $(this).serialize();
|
119
|
+
$.ajax(ajaxOptions);
|
120
|
+
}
|
121
|
+
});
|
122
|
+
}
|
123
|
+
|
124
|
+
function toastMessage(message) {
|
125
|
+
const toastHtml = `
|
126
|
+
<div class="cm-toast-container">
|
127
|
+
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-behaviour="drawer-toast">
|
128
|
+
<div class="d-flex">
|
129
|
+
<div class="toast-body">${message}</div>
|
130
|
+
<button type="button" class="btn-close me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
|
131
|
+
</div>
|
132
|
+
</div>
|
133
|
+
</div>
|
134
|
+
`;
|
135
|
+
$('[data-behaviour="flash-container"]').html(toastHtml);
|
136
|
+
}
|
137
|
+
|
66
138
|
$(document).on("change", '[data-field-type="linked-field"]', function (e) {
|
67
139
|
e.stopPropagation();
|
68
140
|
var params = {};
|
69
141
|
params[$(this).data("field-name")] = $(this).val();
|
70
142
|
var request_url = $(this).data("target-url") + "?" + $.param(params);
|
71
|
-
console.log(request_url);
|
72
143
|
$.ajax(request_url, {
|
73
144
|
type: "GET",
|
74
145
|
success: function (data) {
|
@@ -147,31 +218,31 @@ var replaceAccordionTitle = function (element) {
|
|
147
218
|
.attr("data-bs-target", "#" + accordion_id);
|
148
219
|
$(this).find(".accordion-collapse").attr("id", accordion_id);
|
149
220
|
});
|
150
|
-
|
221
|
+
initializeComponents();
|
151
222
|
};
|
152
223
|
|
153
224
|
export function initializeComponents() {
|
154
|
-
$(
|
225
|
+
$(".select-2").select2({
|
155
226
|
theme: "bootstrap-5",
|
156
227
|
});
|
157
228
|
flatpickr("[data-behaviour='date-only']", {
|
158
|
-
dateFormat: "d-m-Y"
|
159
|
-
})
|
229
|
+
dateFormat: "d-m-Y",
|
230
|
+
});
|
160
231
|
flatpickr("[data-behaviour='date-time']", {
|
161
|
-
enableTime: true
|
162
|
-
})
|
232
|
+
enableTime: true,
|
233
|
+
});
|
163
234
|
flatpickr("[data-behaviour='filter'][data-filter-type='date']", {
|
164
|
-
mode:
|
165
|
-
})
|
166
|
-
var el = document.getElementsByClassName(
|
167
|
-
if(el[0]) {
|
168
|
-
Sortable.create(el[0],{
|
169
|
-
handle:
|
170
|
-
animation: 150
|
235
|
+
mode: "range",
|
236
|
+
});
|
237
|
+
var el = document.getElementsByClassName("columns-list");
|
238
|
+
if (el[0]) {
|
239
|
+
Sortable.create(el[0], {
|
240
|
+
handle: ".dragger",
|
241
|
+
animation: 150,
|
171
242
|
});
|
172
243
|
}
|
173
244
|
|
174
|
-
var headerElemHeight = $(
|
175
|
-
var calculatedHeight = "calc(100vh - " + headerElemHeight+"px"+")"
|
176
|
-
$(
|
177
|
-
}
|
245
|
+
var headerElemHeight = $(".page-top-bar").height() + 64;
|
246
|
+
var calculatedHeight = "calc(100vh - " + headerElemHeight + "px" + ")";
|
247
|
+
$(".table-wrapper").css("maxHeight", calculatedHeight);
|
248
|
+
}
|
@@ -1,110 +1,27 @@
|
|
1
|
-
@import "../helpers/index.scss";
|
2
|
-
|
3
1
|
.cm-drawer {
|
4
|
-
|
5
|
-
|
6
|
-
right: 0;
|
7
|
-
bottom: 0;
|
8
|
-
left: 0;
|
9
|
-
width: 100%;
|
10
|
-
height: 100%;
|
11
|
-
z-index: 5;
|
12
|
-
background-color: rgba(0, 0, 0, 0.7);
|
13
|
-
&__container {
|
14
|
-
position: relative;
|
15
|
-
width: 360px;
|
16
|
-
height: 100%;
|
17
|
-
background: $white;
|
18
|
-
box-shadow: 0px 4px 16px rgba(0, 0, 0, 0.16);
|
19
|
-
.header {
|
20
|
-
display: flex;
|
21
|
-
align-items: center;
|
22
|
-
justify-content: space-between;
|
23
|
-
padding: 24px;
|
24
|
-
background: $grey-lightest-clr;
|
25
|
-
&__lhs {
|
26
|
-
.title {
|
27
|
-
@include font($size: $t1-text, $color: $primary-text-clr, $weight: 600);
|
28
|
-
font-family: $primary-font;
|
29
|
-
line-height: 28px;
|
30
|
-
margin-bottom: 8px;
|
31
|
-
}
|
32
|
-
.description {
|
33
|
-
@include font($size: $t4-text, $color: $primary-text-clr);
|
34
|
-
font-family: $primary-font;
|
35
|
-
line-height: 22px;
|
36
|
-
margin-bottom: 0;
|
37
|
-
}
|
38
|
-
}
|
39
|
-
&__rhs {
|
40
|
-
color: $ink-lighter-clr;
|
41
|
-
}
|
42
|
-
}
|
43
|
-
.body {
|
44
|
-
padding: 24px;
|
45
|
-
.info-text {
|
46
|
-
@include font($size: $t4-text, $color: $primary-text-clr);
|
47
|
-
font-family: $primary-font;
|
48
|
-
line-height: 22px;
|
49
|
-
}
|
50
|
-
}
|
51
|
-
}
|
52
|
-
}
|
53
|
-
|
54
|
-
.drawer-slide-in {
|
55
|
-
animation: slideInLeft 0.3s ease-in forwards;
|
2
|
+
@extend .offcanvas-end;
|
3
|
+
width: 640px !important;
|
56
4
|
}
|
57
5
|
|
58
|
-
.
|
59
|
-
|
6
|
+
.offcanvas-body {
|
7
|
+
padding: 0;
|
60
8
|
}
|
61
9
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
100% {
|
68
|
-
left: 0;
|
69
|
-
opacity: 1;
|
70
|
-
}
|
71
|
-
}
|
72
|
-
|
73
|
-
@keyframes slideOutLeft {
|
74
|
-
0% {
|
75
|
-
left: 0;
|
76
|
-
opacity: 1;
|
77
|
-
}
|
78
|
-
100% {
|
79
|
-
left: -400px;
|
80
|
-
opacity: 0;
|
81
|
-
}
|
10
|
+
.drawer-btn {
|
11
|
+
@extend .btn-link;
|
12
|
+
margin-left: auto;
|
13
|
+
margin-bottom: 0;
|
14
|
+
font-size: $t3-text !important;
|
82
15
|
}
|
83
16
|
|
84
|
-
.drawer-btn {
|
85
|
-
|
86
|
-
|
87
|
-
|
17
|
+
.drawer-btn-group {
|
18
|
+
@extend .d-flex, .flex-column, .gap-2;
|
19
|
+
position: absolute;
|
20
|
+
margin: 16px 0 0 -47px;
|
88
21
|
}
|
89
22
|
|
90
|
-
|
91
|
-
.
|
92
|
-
|
93
|
-
|
94
|
-
justify-content: flex-end;
|
95
|
-
&__actions {
|
96
|
-
display: flex;
|
97
|
-
flex-direction: column;
|
98
|
-
margin: 16px;
|
99
|
-
}
|
100
|
-
&__container {
|
101
|
-
width: 624px;
|
102
|
-
[class^="col-"] {
|
103
|
-
width: 100% !important;
|
104
|
-
}
|
105
|
-
.show-page__inner {
|
106
|
-
height: calc(100vh - 126px);
|
107
|
-
}
|
108
|
-
}
|
109
|
-
}
|
23
|
+
.drawer-nav-btn {
|
24
|
+
@extend .btn-ghost;
|
25
|
+
width: 31px;
|
26
|
+
height: 31px;
|
110
27
|
}
|
@@ -1,19 +1,5 @@
|
|
1
1
|
@import "helpers/index.scss";
|
2
2
|
|
3
|
-
.jGrowl {
|
4
|
-
color: $ink-regular-clr !important;
|
5
|
-
font-size: $t4-text !important;
|
6
|
-
.success {
|
7
|
-
background-color: $green-light-clr !important;
|
8
|
-
}
|
9
|
-
.notice {
|
10
|
-
background-color: $blue-light-clr !important;
|
11
|
-
}
|
12
|
-
.error {
|
13
|
-
background-color: $red-light-clr !important;
|
14
|
-
}
|
15
|
-
}
|
16
|
-
|
17
3
|
.nested-field-wrapper {
|
18
4
|
label {
|
19
5
|
font-size: $t4-text;
|
@@ -85,9 +85,9 @@ module CmAdmin
|
|
85
85
|
redirect_url = request.referrer || cm_admin.send("#{@model.name.underscore}_index_path")
|
86
86
|
respond_to do |format|
|
87
87
|
if @ar_object.destroy
|
88
|
-
format.html { redirect_back fallback_location: redirect_url, notice: "#{
|
88
|
+
format.html { redirect_back fallback_location: redirect_url, notice: "#{@model.formatted_name} was deleted" }
|
89
89
|
else
|
90
|
-
format.html { redirect_back fallback_location: redirect_url,
|
90
|
+
format.html { redirect_back fallback_location: redirect_url, alert: "#{@model.formatted_name} could not be deleted" }
|
91
91
|
end
|
92
92
|
end
|
93
93
|
end
|
@@ -114,12 +114,19 @@ module CmAdmin
|
|
114
114
|
@bulk_action_processor = CmAdmin::BulkActionProcessor.new(@action, @model, params).perform_bulk_action
|
115
115
|
respond_to do |format|
|
116
116
|
if @bulk_action_processor.invalid_records.empty?
|
117
|
-
|
117
|
+
flash[:notice] = "#{@action.formatted_name} was successful"
|
118
|
+
flash[:bulk_action_success] = "<b>#{@action.formatted_name} was successful</b>"
|
119
|
+
format.html { redirect_to request.referrer }
|
118
120
|
else
|
119
121
|
error_messages = @bulk_action_processor.invalid_records.map do |invalid_record|
|
120
122
|
"<li>#{invalid_record.error_message}</li>"
|
121
123
|
end.join
|
122
|
-
|
124
|
+
flash[:alert] = "#{@action.formatted_name} was unsuccessful"
|
125
|
+
flash[:bulk_action_error] = "<div>
|
126
|
+
<div><b>#{@action.formatted_name} encountered the following errors:</b></div>
|
127
|
+
<ul>#{error_messages}</ul>
|
128
|
+
</div>"
|
129
|
+
format.html { redirect_to request.referrer }
|
123
130
|
end
|
124
131
|
end
|
125
132
|
end
|
@@ -161,13 +168,13 @@ module CmAdmin
|
|
161
168
|
format.json { render json: response_object }
|
162
169
|
elsif response_object.errors.empty?
|
163
170
|
redirect_url = @model.current_action.redirection_url || @action.redirection_url || request.referrer || "/cm_admin/#{@model.ar_model.table_name}/#{@response_object.id}"
|
164
|
-
format.html { redirect_to redirect_url, notice: "#{@action.
|
171
|
+
format.html { redirect_to redirect_url, notice: "#{@action.formatted_name} was successful" }
|
165
172
|
else
|
166
173
|
error_messages = response_object.errors.full_messages.map { |error_message| "<li>#{error_message}</li>" }.join
|
167
|
-
format.html { redirect_to request.referrer, alert: "
|
174
|
+
format.html { redirect_to request.referrer, alert: "#{@action.formatted_name} was unsuccessful<br /><ul>#{error_messages}</ul>" }
|
168
175
|
end
|
169
176
|
rescue StandardError => e
|
170
|
-
format.html { redirect_to request.referrer, alert: "<div
|
177
|
+
format.html { redirect_to request.referrer, alert: "<div>#{@action.formatted_name} was unsuccessful<br /><ul><li>#{e.message}</li></ul></div>" }
|
171
178
|
end
|
172
179
|
end
|
173
180
|
end
|
@@ -191,6 +198,16 @@ module CmAdmin
|
|
191
198
|
@model.default_sort_direction = nil
|
192
199
|
end
|
193
200
|
|
201
|
+
def fetch_drawer
|
202
|
+
@model = Model.find_by({ name: controller_name.classify })
|
203
|
+
return if @model.blank?
|
204
|
+
|
205
|
+
action_page_title = CmAdmin::Models::Action.find_by(@model, name: 'new').page_title
|
206
|
+
drawer_title = action_page_title.presence || "New #{@model&.formatted_name}"
|
207
|
+
@ar_object = @model.ar_model.new
|
208
|
+
render partial: 'layouts/drawer', locals: { drawer_title:, from_field_id: params[:from_field_id] }
|
209
|
+
end
|
210
|
+
|
194
211
|
def get_nested_table_fields(fields)
|
195
212
|
nested_table_fields = []
|
196
213
|
fields.each do |field|
|
@@ -233,9 +250,28 @@ module CmAdmin
|
|
233
250
|
cm_admin.send("#{@model.name.underscore}_show_path", @ar_object)
|
234
251
|
end
|
235
252
|
ActiveStorage::Attachment.where(id: params['attachment_destroy_ids']).destroy_all if params['attachment_destroy_ids'].present?
|
236
|
-
|
253
|
+
notice = if @current_action&.name == 'new'
|
254
|
+
"#{@model&.formatted_name} was created"
|
255
|
+
elsif @current_action&.name == 'edit'
|
256
|
+
"#{@model&.formatted_name} was updated"
|
257
|
+
else
|
258
|
+
"#{@action&.formatted_name} #{@model&.formatted_name} was successful"
|
259
|
+
end
|
260
|
+
format.html { redirect_to redirect_url, allow_other_host: true, notice: }
|
261
|
+
name = if @ar_object.respond_to?(:formatted_name)
|
262
|
+
@ar_object.formatted_name
|
263
|
+
elsif @ar_object.respond_to?(:name)
|
264
|
+
@ar_object.name
|
265
|
+
else
|
266
|
+
@ar_object&.id
|
267
|
+
end
|
268
|
+
format.json { render json: { message: notice, data: { id: @ar_object&.id, name: } }, status: :ok }
|
237
269
|
else
|
238
|
-
format.html { render '/cm_admin/main/new', notice: "#{
|
270
|
+
format.html { render '/cm_admin/main/new', notice: "#{@action&.formatted_name} #{@model&.formatted_name} was unsuccessful" }
|
271
|
+
format.json do
|
272
|
+
formatted_error_response = @ar_object.errors.full_messages.map { |error_message| "<li>#{error_message}</li>" }.join
|
273
|
+
render json: { message: formatted_error_response }, status: :unprocessable_entity
|
274
|
+
end
|
239
275
|
end
|
240
276
|
end
|
241
277
|
end
|
@@ -13,12 +13,13 @@
|
|
13
13
|
- if @associated_model.sort_columns.present?
|
14
14
|
= render 'cm_admin/main/sort', model: @associated_model, ar_object: @associated_ar_object
|
15
15
|
|
16
|
-
- if flash[:
|
17
|
-
.alert.alert-danger role="alert"
|
18
|
-
= flash[:
|
19
|
-
-
|
20
|
-
.alert.alert-success
|
21
|
-
= flash[:
|
16
|
+
- if flash[:bulk_action_error].present?
|
17
|
+
.alert.alert-danger.me-4 role="alert"
|
18
|
+
= flash[:bulk_action_error].html_safe
|
19
|
+
- if flash[:bulk_action_success].present?
|
20
|
+
.alert.alert-success.me-4 role="alert"
|
21
|
+
= flash[:bulk_action_success].html_safe
|
22
|
+
|
22
23
|
- bulk_actions = actions_filter(@associated_model, @associated_ar_object, :bulk_action)
|
23
24
|
- if bulk_actions.present?
|
24
25
|
.table-top.hidden data-section="bulk-action"
|
@@ -8,17 +8,6 @@
|
|
8
8
|
i.fa.fa-table
|
9
9
|
a.btn.btn-ghost href="#{cm_admin.send("#{@model.name.underscore}_index_path")}?page=#{params[:page] || 1}&view_type=card"
|
10
10
|
i.fa.fa-table-cells
|
11
|
-
/ button.secondary-btn.column-btn data-bs-target="#columnActionModal" data-bs-toggle="modal"
|
12
|
-
/ span
|
13
|
-
/ i.fa.fa-columns.bolder
|
14
|
-
/ span
|
15
|
-
/ i.fa.fa-angle-down
|
16
|
-
- if flash[:alert].present?
|
17
|
-
.alert.alert-danger role="alert"
|
18
|
-
= flash[:alert].html_safe
|
19
|
-
- elsif flash[:notice].present?
|
20
|
-
.alert.alert-success
|
21
|
-
= flash[:notice].html_safe
|
22
11
|
|
23
12
|
- bulk_actions = actions_filter(@model, @ar_object, :bulk_action)
|
24
13
|
- if bulk_actions.present?
|
@@ -1,10 +1,3 @@
|
|
1
|
-
- if flash[:alert].present?
|
2
|
-
.alert.alert-danger role="alert"
|
3
|
-
= flash[:alert].html_safe
|
4
|
-
- elsif flash[:notice].present?
|
5
|
-
.alert.alert-success
|
6
|
-
= flash[:notice].html_safe
|
7
|
-
|
8
1
|
.kanban-container
|
9
2
|
- column_names = @ar_object.data.keys
|
10
3
|
= hidden_field_tag :kanban_column_names, column_names
|
@@ -7,16 +7,15 @@
|
|
7
7
|
i.fa.fa-table
|
8
8
|
a.btn.btn-ghost href="#{cm_admin.send("#{@model.name.underscore}_index_path")}?page=#{params[:page] || 1}&view_type=card"
|
9
9
|
i.fa.fa-table-cells
|
10
|
-
|
11
10
|
- if @model.sort_columns.present?
|
12
|
-
|
11
|
+
= render 'cm_admin/main/sort', model: @model, ar_object: @ar_object
|
13
12
|
|
14
|
-
- if flash[:
|
13
|
+
- if flash[:bulk_action_success].present?
|
14
|
+
.alert.alert-success.me-4 role="success"
|
15
|
+
= flash[:bulk_action_success].html_safe
|
16
|
+
- if flash[:bulk_action_error].present?
|
15
17
|
.alert.alert-danger.me-4 role="alert"
|
16
|
-
= flash[:
|
17
|
-
- elsif flash[:notice].present?
|
18
|
-
.alert.alert-success.me-4
|
19
|
-
= flash[:notice].html_safe
|
18
|
+
= flash[:bulk_action_error].html_safe
|
20
19
|
|
21
20
|
- bulk_actions = actions_filter(@model, @ar_object, :bulk_action)
|
22
21
|
- if bulk_actions.present?
|
@@ -1,3 +1,4 @@
|
|
1
|
+
.drawer-container data-behaviour="cm-drawer-container"
|
1
2
|
.form-page
|
2
3
|
.form-page__header
|
3
4
|
== render 'cm_admin/main/top_navbar'
|
@@ -5,9 +6,9 @@
|
|
5
6
|
- if @ar_object.errors.present?
|
6
7
|
.flag-alert.mb-4
|
7
8
|
p.alert-header
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
span
|
10
|
+
i.fa.fa-exclamation-triangle
|
11
|
+
| Attention
|
11
12
|
.alert-body
|
12
13
|
p.body-title
|
13
14
|
| Error: unable to save #{@ar_object.class.name.downcase}
|
@@ -1,9 +1,6 @@
|
|
1
1
|
- if flash[:notice].present?
|
2
|
-
|
3
|
-
$.jGrowl("#{flash[:notice]}", { theme: 'notice' })
|
2
|
+
= toast_message(flash[:notice], 'notice')
|
4
3
|
- elsif flash[:success].present?
|
5
|
-
|
6
|
-
$.jGrowl("#{flash[:success]}", { theme: 'success' })
|
4
|
+
= toast_message(flash[:success], 'success')
|
7
5
|
- elsif flash[:alert].present?
|
8
|
-
|
9
|
-
$.jGrowl("#{flash[:alert].html_safe}", { theme: 'error' })
|
6
|
+
= toast_message(flash[:alert], 'alert')
|
@@ -0,0 +1,23 @@
|
|
1
|
+
.offcanvas.cm-drawer tabindex="-1" id="cm-drawer" aria-labelledby="offcanvasExampleLabel" data-behavior="cm-drawer" data-from-field-id="#{params[:from_field_id]}"
|
2
|
+
.drawer-btn-group
|
3
|
+
button.drawer-nav-btn type="button" data-bs-dismiss="offcanvas" aria-label="Close"
|
4
|
+
i.fa-solid.fa-x
|
5
|
+
.offcanvas-header
|
6
|
+
h4 = drawer_title
|
7
|
+
.offcanvas-body
|
8
|
+
.form-page
|
9
|
+
.form-page__body
|
10
|
+
.flag-alert.mb-4.hidden data-behavior="flag-alert"
|
11
|
+
p.alert-header
|
12
|
+
span
|
13
|
+
i.fa.fa-exclamation-triangle
|
14
|
+
| Attention
|
15
|
+
.alert-body
|
16
|
+
p.body-title
|
17
|
+
| Error: unable to save #{@ar_object.class.name.downcase}
|
18
|
+
p.body-info
|
19
|
+
ul data-behavior="error-list"
|
20
|
+
- @ar_object.errors.full_messages.each do |error_message|
|
21
|
+
li = error_message
|
22
|
+
- if @model.present?
|
23
|
+
= generate_form(@ar_object, @model, is_drawer_form: true)
|
data/config/importmap.rb
CHANGED
@@ -3,7 +3,6 @@ pin 'jquery', to: 'https://ga.jspm.io/npm:jquery@3.6.0/dist/jquery.js', preload:
|
|
3
3
|
pin 'bootstrap', to: 'https://ga.jspm.io/npm:bootstrap@5.1.3/dist/js/bootstrap.esm.js', preload: true
|
4
4
|
pin '@popperjs/core', to: 'https://ga.jspm.io/npm:@popperjs/core@2.11.5/lib/index.js', preload: true
|
5
5
|
pin 'flatpickr', to: 'https://ga.jspm.io/npm:flatpickr@4.6.13/dist/esm/index.js'
|
6
|
-
pin 'jgrowl', to: 'https://ga.jspm.io/npm:jgrowl@1.4.8/jquery.jgrowl.js'
|
7
6
|
pin 'moment', to: 'https://ga.jspm.io/npm:moment@2.29.4/moment.js'
|
8
7
|
pin 'trix', to: 'https://ga.jspm.io/npm:trix@2.1.3/dist/trix.esm.min.js'
|
9
8
|
pin '@rails/actiontext', to: 'https://ga.jspm.io/npm:@rails/actiontext@7.1.3-4/app/assets/javascripts/actiontext.esm.js'
|
data/config/routes.rb
CHANGED
@@ -23,6 +23,10 @@ CmAdmin::Engine.routes.draw do
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
+
scope model.name.tableize do
|
27
|
+
send(:get, 'fetch_drawer', to: "#{model.name.underscore}#fetch_drawer", as: "cm_fetch_drawer_#{model.name.underscore}")
|
28
|
+
end
|
29
|
+
|
26
30
|
model.available_actions.sort_by { |act| act.name }.each do |act|
|
27
31
|
scope model.name.tableize do
|
28
32
|
# Define route only when action trail related field is present
|
data/lib/cm_admin/model.rb
CHANGED
@@ -5,9 +5,9 @@ module CmAdmin
|
|
5
5
|
class Action
|
6
6
|
include Actions::Blocks
|
7
7
|
attr_accessor :name, :display_name, :verb, :layout_type, :layout, :partial, :path, :page_title, :page_description,
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
:child_records, :is_nested_field, :nested_table_name, :parent, :display_if, :route_type, :code_block,
|
9
|
+
:display_type, :action_type, :redirection_url, :sort_direction, :sort_column, :icon_name, :scopes, :view_type,
|
10
|
+
:kanban_attr, :model_name, :redirect_to
|
11
11
|
|
12
12
|
VALID_SORT_DIRECTION = Set[:asc, :desc].freeze
|
13
13
|
|
@@ -23,14 +23,14 @@ module CmAdmin
|
|
23
23
|
end
|
24
24
|
set_default_values
|
25
25
|
attributes.each do |key, value|
|
26
|
-
|
26
|
+
send("#{key}=", value)
|
27
27
|
end
|
28
|
-
|
28
|
+
send('code_block=', block) if block_given?
|
29
29
|
end
|
30
30
|
|
31
31
|
def set_default_values
|
32
32
|
self.is_nested_field = false
|
33
|
-
self.display_if =
|
33
|
+
self.display_if = ->(_arg) { true }
|
34
34
|
self.display_type = :button
|
35
35
|
self.action_type = :default
|
36
36
|
self.sort_column = :created_at
|
@@ -44,7 +44,7 @@ module CmAdmin
|
|
44
44
|
self.kanban_attr = {}
|
45
45
|
end
|
46
46
|
|
47
|
-
def set_values(page_title, page_description, partial, redirect_to = nil, view_type
|
47
|
+
def set_values(page_title, page_description, partial, redirect_to = nil, view_type = :table)
|
48
48
|
self.page_title = page_title
|
49
49
|
self.page_description = page_description
|
50
50
|
self.partial = partial
|
@@ -64,6 +64,10 @@ module CmAdmin
|
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
67
|
+
def formatted_name
|
68
|
+
display_name || name.titleize
|
69
|
+
end
|
70
|
+
|
67
71
|
class << self
|
68
72
|
def find_by(model, search_hash)
|
69
73
|
model.available_actions.find { |i| i.name == search_hash[:name] }
|
@@ -7,7 +7,7 @@ module CmAdmin
|
|
7
7
|
|
8
8
|
attr_accessor :field_name, :label, :header, :input_type, :collection, :disabled, :helper_method,
|
9
9
|
:placeholder, :display_if, :html_attrs, :target, :col_size, :ajax_url, :helper_text,
|
10
|
-
:image_preview
|
10
|
+
:image_preview, :can_create_new_entity
|
11
11
|
|
12
12
|
VALID_INPUT_TYPES = %i[
|
13
13
|
integer decimal string single_select multi_select date date_time text switch custom_single_select checkbox_group
|
@@ -34,6 +34,7 @@ module CmAdmin
|
|
34
34
|
self.target = {}
|
35
35
|
self.col_size = nil
|
36
36
|
self.image_preview = false
|
37
|
+
self.can_create_new_entity = false
|
37
38
|
end
|
38
39
|
|
39
40
|
def set_default_placeholder
|
data/lib/cm_admin/version.rb
CHANGED
@@ -20,7 +20,7 @@ module CmAdmin
|
|
20
20
|
def add_filters_dropdown(filters)
|
21
21
|
concat(content_tag(:button, class: 'dropdown btn-ghost', data: { bs_toggle: 'dropdown' }) do
|
22
22
|
concat tag.i(class: 'fas fa-filter')
|
23
|
-
concat tag.span '
|
23
|
+
concat tag.span 'Filters'
|
24
24
|
end)
|
25
25
|
|
26
26
|
concat(content_tag(:div, class: 'dropdown-menu dropdown-popup') do
|
@@ -32,7 +32,7 @@ module CmAdmin
|
|
32
32
|
concat(content_tag(:div, class: 'list-area') do
|
33
33
|
filters.each do |filter|
|
34
34
|
concat(content_tag(:div, class: 'pointer list-item', data: { behaviour: 'filter-option', filter_type: "#{filter.filter_type}", db_column: "#{filter.db_column_name}" }) do
|
35
|
-
tag.span filter.
|
35
|
+
tag.span filter.display_name.to_s
|
36
36
|
end)
|
37
37
|
end
|
38
38
|
end)
|
@@ -7,8 +7,9 @@ module CmAdmin
|
|
7
7
|
module FormHelper
|
8
8
|
include FormFieldHelper
|
9
9
|
REJECTABLE = %w[id created_at updated_at]
|
10
|
+
attr_accessor :is_drawer_form
|
10
11
|
|
11
|
-
def generate_form(resource, cm_model)
|
12
|
+
def generate_form(resource, cm_model, is_drawer_form: false)
|
12
13
|
if resource.new_record?
|
13
14
|
action = :new
|
14
15
|
method = :post
|
@@ -16,6 +17,7 @@ module CmAdmin
|
|
16
17
|
action = :edit
|
17
18
|
method = :patch
|
18
19
|
end
|
20
|
+
@is_drawer_form = is_drawer_form
|
19
21
|
return form_with_all_fields(resource, method) if cm_model.available_fields[action].empty?
|
20
22
|
|
21
23
|
form_with_mentioned_fields(resource, cm_model.available_fields[action], method)
|
@@ -66,6 +68,7 @@ module CmAdmin
|
|
66
68
|
def create_sections(resource, form_obj, section)
|
67
69
|
content_tag :div, class: 'col form-container' do
|
68
70
|
return render partial: section.partial, locals: { form_obj: } if section.partial
|
71
|
+
|
69
72
|
concat content_tag(:p, section.section_name, class: 'form-title') unless section.parent_section.present?
|
70
73
|
concat set_form_for_fields(resource, form_obj, section)
|
71
74
|
end
|
@@ -116,10 +119,7 @@ module CmAdmin
|
|
116
119
|
concat input_field_for_column(form_obj, field, is_required:)
|
117
120
|
else
|
118
121
|
concat(content_tag(:div, class: "form-field #{field.disabled ? 'disabled' : ''}") do
|
119
|
-
|
120
|
-
concat form_obj.label field.label, field.label, class: "field-label #{is_required ? 'required-label' : ''}"
|
121
|
-
concat tag.br
|
122
|
-
end
|
122
|
+
concat set_form_label(field, form_obj, is_required)
|
123
123
|
concat input_field_for_column(form_obj, field, is_required:)
|
124
124
|
concat tag.small field.helper_text, class: 'form-text text-muted' if field.helper_text.present?
|
125
125
|
concat tag.p resource.errors[field.field_name].first if resource.errors[field.field_name].present?
|
@@ -128,6 +128,25 @@ module CmAdmin
|
|
128
128
|
end
|
129
129
|
end
|
130
130
|
|
131
|
+
def set_form_label(field, form_obj, is_required)
|
132
|
+
return unless field.label && %i[check_box switch].exclude?(field.input_type)
|
133
|
+
|
134
|
+
content_tag(:div, class: 'd-flex') do
|
135
|
+
concat form_obj.label field.label, field.label, class: "field-label #{is_required ? 'required-label' : ''}"
|
136
|
+
concat set_drawer_form_button(field, form_obj)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def set_drawer_form_button(field, form_obj)
|
141
|
+
model_name = find_model_name(form_obj, field)
|
142
|
+
ar_object = model_name.constantize.new if model_name.present?
|
143
|
+
return unless field.input_type.to_s == 'single_select' && model_name.present? && has_valid_policy(ar_object, 'new')
|
144
|
+
|
145
|
+
field_id = form_obj.field_id(field.field_name.to_sym)
|
146
|
+
drawer_fetch_url = "#{CmAdmin::Engine.mount_path}/#{model_name.tableize}/fetch_drawer?from_field_id=#{field_id}"
|
147
|
+
tag.div 'New Entity', class: 'drawer-btn', data: { behaviour: 'offcanvas', drawer_fetch_url: }
|
148
|
+
end
|
149
|
+
|
131
150
|
def set_nested_section_form_fields(resource, form_obj, nested_sections)
|
132
151
|
return if nested_sections.blank?
|
133
152
|
|
@@ -142,7 +161,7 @@ module CmAdmin
|
|
142
161
|
def set_nested_form_fields(form_obj, section)
|
143
162
|
content_tag(:div) do
|
144
163
|
section.nested_table_fields.each do |nested_table_field|
|
145
|
-
concat(render(partial: '/cm_admin/main/nested_table_form', locals: { f: form_obj, nested_table_field:
|
164
|
+
concat(render(partial: '/cm_admin/main/nested_table_form', locals: { f: form_obj, nested_table_field: }))
|
146
165
|
end
|
147
166
|
end
|
148
167
|
end
|
@@ -150,7 +169,7 @@ module CmAdmin
|
|
150
169
|
def set_form_with_sections(resource, entities, url, method)
|
151
170
|
url_with_query_params = extract_query_params(url)
|
152
171
|
|
153
|
-
form_for(resource, url: url_with_query_params || url, method
|
172
|
+
form_for(resource, url: url_with_query_params || url, method:, html: { class: "cm_#{resource.class.name.downcase}_form" }, data: { is_drawer_form: @is_drawer_form }) do |form_obj|
|
154
173
|
concat form_obj.text_field 'referrer', class: 'normal-input', hidden: true, value: params[:referrer], name: 'referrer' if params[:referrer]
|
155
174
|
if params[:polymorphic_name].present?
|
156
175
|
concat form_obj.text_field params[:polymorphic_name] + '_type', class: 'normal-input', hidden: true, value: params[:associated_class].classify
|
@@ -162,7 +181,7 @@ module CmAdmin
|
|
162
181
|
concat split_form_into_section(resource, form_obj, entities)
|
163
182
|
concat tag.br
|
164
183
|
concat form_obj.submit 'Save', class: 'btn-cta', data: { behaviour: 'form_submit', form_class: "cm_#{form_obj.object.class.name.downcase}_form" }
|
165
|
-
concat button_tag 'Discard', class: 'btn-secondary discard-form', data: { behaviour: 'discard_form' }
|
184
|
+
concat button_tag 'Discard', class: 'btn-secondary discard-form', data: { behaviour: 'discard_form' } unless @is_drawer_form
|
166
185
|
end
|
167
186
|
end
|
168
187
|
|
@@ -184,6 +203,16 @@ module CmAdmin
|
|
184
203
|
end
|
185
204
|
form_obj.object._validators[field.field_name].map(&:kind).include?(:presence) || associated_field.present?
|
186
205
|
end
|
206
|
+
|
207
|
+
def find_model_name(form_obj, field)
|
208
|
+
return unless field.can_create_new_entity.presence
|
209
|
+
return field.can_create_new_entity[:model] if field.can_create_new_entity.is_a?(Hash) && field.can_create_new_entity[:model].present?
|
210
|
+
|
211
|
+
associated_field = form_obj.object.class.reflect_on_all_associations(:belongs_to).select do |association|
|
212
|
+
"#{association.name}_id" == field.field_name.to_s
|
213
|
+
end
|
214
|
+
associated_field&.first&.klass&.name
|
215
|
+
end
|
187
216
|
end
|
188
217
|
end
|
189
218
|
end
|
@@ -38,21 +38,26 @@ module CmAdmin
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def custom_action_items(custom_action, current_action_name)
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
41
|
+
return unless custom_action.name.present? && policy([:cm_admin, @model.name.classify.constantize]).send(:"#{custom_action.name}?")
|
42
|
+
|
43
|
+
scoped_model = "CmAdmin::#{@model.name}Policy::#{custom_action.name.classify}Scope".constantize.new(Current.user, @model.name.constantize).resolve
|
44
|
+
has_scoped_record = if current_action_name == 'index'
|
45
|
+
scoped_model.present?
|
46
|
+
else
|
47
|
+
scoped_model.find_by(id: params[:id]).present?
|
48
|
+
end
|
49
|
+
return unless custom_action.display_if.call(@ar_object) && has_scoped_record
|
50
|
+
|
51
|
+
case custom_action.display_type
|
52
|
+
when :icon_only
|
53
|
+
custom_action_icon(custom_action, current_action_name)
|
54
|
+
when :button
|
55
|
+
custom_action_button(custom_action, current_action_name)
|
56
|
+
when :modal
|
57
|
+
custom_modal_button(custom_action)
|
58
|
+
when :page
|
59
|
+
path = cm_admin.send("#{@model.name.underscore}_#{custom_action.name}_path", @ar_object.id, custom_action.url_params)
|
60
|
+
link_to custom_action_title(custom_action), path, class: 'btn-secondary ms-2', method: custom_action.verb
|
56
61
|
end
|
57
62
|
end
|
58
63
|
|
@@ -66,7 +71,7 @@ module CmAdmin
|
|
66
71
|
|
67
72
|
def custom_action_button(custom_action, current_action_name)
|
68
73
|
if current_action_name == "index"
|
69
|
-
button_to custom_action_title(custom_action), @model.ar_model.table_name + '/' + custom_action.path, class: 'btn-secondary ms-2', method: custom_action.verb
|
74
|
+
button_to custom_action_title(custom_action), @model.ar_model.table_name + '/' + (custom_action.path || custom_action.name), class: 'btn-secondary ms-2', method: custom_action.verb
|
70
75
|
elsif current_action_name == "show"
|
71
76
|
button_to custom_action_title(custom_action), custom_action.path.gsub(':id', params[:id]), class: 'btn-secondary ms-2', method: custom_action.verb
|
72
77
|
end
|
@@ -83,6 +83,17 @@ module CmAdmin
|
|
83
83
|
"#{count} #{table_name.humanize.downcase} found"
|
84
84
|
end
|
85
85
|
|
86
|
+
def toast_message(message, toast_type)
|
87
|
+
tag.div class: 'position-fixed bottom-0 end-0 p-3', style: 'z-index: 11' do
|
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
|
+
tag.div class: 'd-flex' do
|
90
|
+
concat tag.div message.html_safe, class: 'toast-body'
|
91
|
+
concat tag.button '', type: 'button', class: "btn-close me-2 m-auto #{toast_type == 'alert' ? 'btn-close-white' : ''}", data: { 'bs-dismiss': 'toast' }, 'aria-label': 'Close'
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
86
97
|
def project_name_with_env
|
87
98
|
return CmAdmin.config.project_name if Rails.env.production?
|
88
99
|
|
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: 2.
|
4
|
+
version: 2.4.0
|
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-10-
|
17
|
+
date: 2024-10-14 00:00:00.000000000 Z
|
18
18
|
dependencies:
|
19
19
|
- !ruby/object:Gem::Dependency
|
20
20
|
name: caxlsx_rails
|
@@ -344,7 +344,6 @@ files:
|
|
344
344
|
- app/assets/stylesheets/cm_admin/dependency/bootstrap/scss/utilities/_api.scss
|
345
345
|
- app/assets/stylesheets/cm_admin/dependency/bootstrap/scss/vendor/_rfs.scss
|
346
346
|
- app/assets/stylesheets/cm_admin/dependency/flatpickr.min.css
|
347
|
-
- app/assets/stylesheets/cm_admin/dependency/jquery-jgrowl.min.css
|
348
347
|
- app/assets/stylesheets/cm_admin/helpers/_mixins.scss
|
349
348
|
- app/assets/stylesheets/cm_admin/helpers/_variable.scss
|
350
349
|
- app/assets/stylesheets/cm_admin/helpers/index.scss
|
@@ -375,7 +374,6 @@ files:
|
|
375
374
|
- app/views/cm_admin/main/_card.html.slim
|
376
375
|
- app/views/cm_admin/main/_cm_pagy_nav.html.slim
|
377
376
|
- app/views/cm_admin/main/_custom_action_modal_form.html.slim
|
378
|
-
- app/views/cm_admin/main/_drawer.html.slim
|
379
377
|
- app/views/cm_admin/main/_filters.html.slim
|
380
378
|
- app/views/cm_admin/main/_kanban.html.slim
|
381
379
|
- app/views/cm_admin/main/_member_custom_action_modal.html.slim
|
@@ -406,6 +404,7 @@ files:
|
|
406
404
|
- app/views/layouts/_custom_action_modal.html.slim
|
407
405
|
- app/views/layouts/_custom_action_modals.html.slim
|
408
406
|
- app/views/layouts/_destroy_action_modal.html.slim
|
407
|
+
- app/views/layouts/_drawer.html.slim
|
409
408
|
- app/views/layouts/_left_sidebar_nav.html.slim
|
410
409
|
- app/views/layouts/_quick_links.html.slim
|
411
410
|
- app/views/layouts/cm_admin.html.slim
|
@@ -1 +0,0 @@
|
|
1
|
-
.jGrowl{z-index:9999;color:#fff;font-size:12px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;position:fixed}.jGrowl.top-left{left:0;top:0}.jGrowl.top-right{right:0;top:0}.jGrowl.bottom-left{left:0;bottom:0}.jGrowl.bottom-right{right:0;bottom:0}.jGrowl.center{top:0;width:50%;left:25%}.jGrowl.center .jGrowl-closer,.jGrowl.center .jGrowl-notification{margin-left:auto;margin-right:auto}.jGrowl-notification{background-color:#000;opacity:.9;zoom:1;width:250px;padding:10px;margin:10px;text-align:left;display:none;border-radius:5px;min-height:40px}.jGrowl-notification .ui-state-highlight,.jGrowl-notification .ui-widget-content .ui-state-highlight,.jGrowl-notification .ui-widget-header .ui-state-highlight{border:1px solid #000;background:#000;color:#fff}.jGrowl-notification .jGrowl-header{font-weight:700;font-size:.85em}.jGrowl-notification .jGrowl-close{background-color:transparent;color:inherit;border:none;z-index:99;float:right;font-weight:700;font-size:1em;cursor:pointer}.jGrowl-closer{background-color:#000;opacity:.9;zoom:1;width:250px;padding:10px;margin:10px;text-align:left;display:none;border-radius:5px;padding-top:4px;padding-bottom:4px;cursor:pointer;font-size:.9em;font-weight:700;text-align:center}.jGrowl-closer .ui-state-highlight,.jGrowl-closer .ui-widget-content .ui-state-highlight,.jGrowl-closer .ui-widget-header .ui-state-highlight{border:1px solid #000;background:#000;color:#fff}@media print{.jGrowl{display:none}}
|
@@ -1,13 +0,0 @@
|
|
1
|
-
.cm-drawer.hidden
|
2
|
-
.cm-drawer__container#drawer-container
|
3
|
-
.header
|
4
|
-
.header__lhs
|
5
|
-
h2.title Review
|
6
|
-
p.description Theresa Webb
|
7
|
-
.header__rhs
|
8
|
-
i.fa.fa-times.drawer-close.pointer
|
9
|
-
.body
|
10
|
-
p.info-text
|
11
|
-
| It was really an amazing experience being an Unschooler. Its really different from other e-learning websites in every aspects like certification, providing internship opportunities and many more. I am going to recommend Unschool for sure!
|
12
|
-
|
13
|
-
|