madmin 1.2.11 → 2.0.1
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/README.md +36 -7
- data/app/assets/config/madmin_manifest.js +3 -0
- data/app/assets/stylesheets/madmin/actiontext.css +31 -0
- data/app/assets/stylesheets/madmin/application-sprockets.css +11 -0
- data/app/assets/stylesheets/madmin/application.css +10 -0
- data/app/assets/stylesheets/madmin/base.css +117 -0
- data/app/assets/stylesheets/madmin/buttons.css +46 -0
- data/app/assets/stylesheets/madmin/forms.css +64 -0
- data/app/assets/stylesheets/madmin/pagination.css +59 -0
- data/app/assets/stylesheets/madmin/reset.css +242 -0
- data/app/assets/stylesheets/madmin/sidebar.css +80 -0
- data/app/assets/stylesheets/madmin/tables.css +56 -0
- data/app/controllers/madmin/application_controller.rb +7 -12
- data/app/controllers/madmin/base_controller.rb +1 -0
- data/app/controllers/madmin/resource_controller.rb +7 -1
- data/app/helpers/madmin/application_helper.rb +1 -12
- data/app/helpers/madmin/sort_helper.rb +17 -1
- data/app/javascript/madmin/application.js +4 -0
- data/app/javascript/madmin/controllers/application.js +12 -0
- data/app/javascript/madmin/controllers/index.js +5 -0
- data/app/javascript/madmin/controllers/nested_form_controller.js +34 -0
- data/app/javascript/madmin/controllers/select_controller.js +32 -0
- data/app/views/layouts/madmin/application.html.erb +12 -13
- data/app/views/madmin/application/_flash.html.erb +13 -0
- data/app/views/madmin/application/_form.html.erb +13 -12
- data/app/views/madmin/application/_javascript.html.erb +3 -136
- data/app/views/madmin/application/_navigation.html.erb +22 -27
- data/app/views/madmin/application/edit.html.erb +9 -5
- data/app/views/madmin/application/index.html.erb +37 -31
- data/app/views/madmin/application/new.html.erb +9 -5
- data/app/views/madmin/application/show.html.erb +28 -22
- data/app/views/madmin/dashboard/show.html.erb +4 -1
- data/app/views/madmin/fields/attachment/_form.html.erb +11 -4
- data/app/views/madmin/fields/attachment/_index.html.erb +5 -1
- data/app/views/madmin/fields/attachment/_show.html.erb +4 -4
- data/app/views/madmin/fields/attachments/_form.html.erb +1 -4
- data/app/views/madmin/fields/belongs_to/_form.html.erb +1 -4
- data/app/views/madmin/fields/belongs_to/_index.html.erb +2 -1
- data/app/views/madmin/fields/boolean/_form.html.erb +3 -4
- data/app/views/madmin/fields/currency/_form.html.erb +1 -0
- data/app/views/madmin/fields/currency/_index.html.erb +1 -0
- data/app/views/madmin/fields/currency/_show.html.erb +1 -0
- data/app/views/madmin/fields/date/_form.html.erb +1 -4
- data/app/views/madmin/fields/date_time/_form.html.erb +1 -4
- data/app/views/madmin/fields/decimal/_form.html.erb +1 -4
- data/app/views/madmin/fields/enum/_form.html.erb +1 -4
- data/app/views/madmin/fields/file/_form.html.erb +1 -4
- data/app/views/madmin/fields/float/_form.html.erb +1 -4
- data/app/views/madmin/fields/has_many/_form.html.erb +1 -4
- data/app/views/madmin/fields/has_many/_show.html.erb +7 -1
- data/app/views/madmin/fields/has_one/_form.html.erb +0 -3
- data/app/views/madmin/fields/integer/_form.html.erb +1 -4
- data/app/views/madmin/fields/integer/_index.html.erb +1 -5
- data/app/views/madmin/fields/json/_form.html.erb +1 -4
- data/app/views/madmin/fields/nested_has_many/_fields.html.erb +4 -5
- data/app/views/madmin/fields/nested_has_many/_form.html.erb +0 -4
- data/app/views/madmin/fields/nested_has_many/_show.html.erb +7 -1
- data/app/views/madmin/fields/password/_form.html.erb +1 -4
- data/app/views/madmin/fields/polymorphic/_form.html.erb +1 -4
- data/app/views/madmin/fields/rich_text/_form.html.erb +1 -6
- data/app/views/madmin/fields/select/_form.html.erb +1 -0
- data/app/views/madmin/fields/select/_index.html.erb +1 -0
- data/app/views/madmin/fields/select/_show.html.erb +1 -0
- data/app/views/madmin/fields/string/_form.html.erb +1 -4
- data/app/views/madmin/fields/text/_form.html.erb +1 -4
- data/app/views/madmin/shared/_label.html.erb +2 -2
- data/config/importmap.rb +10 -0
- data/lib/generators/madmin/field/templates/_form.html.erb +1 -4
- data/lib/generators/madmin/install/templates/controller.rb.tt +5 -12
- data/lib/generators/madmin/resource/resource_generator.rb +1 -1
- data/lib/generators/madmin/resource/templates/resource.rb.tt +12 -10
- data/lib/madmin/engine.rb +26 -0
- data/lib/madmin/field.rb +21 -5
- data/lib/madmin/fields/belongs_to.rb +10 -5
- data/lib/madmin/fields/currency.rb +15 -0
- data/lib/madmin/fields/has_many.rb +17 -0
- data/lib/madmin/fields/nested_has_many.rb +5 -1
- data/lib/madmin/fields/polymorphic.rb +1 -1
- data/lib/madmin/fields/select.rb +9 -0
- data/lib/madmin/generator_helpers.rb +6 -2
- data/lib/madmin/menu.rb +70 -0
- data/lib/madmin/resource.rb +56 -24
- data/lib/madmin/search.rb +1 -1
- data/lib/madmin/version.rb +1 -1
- data/lib/madmin.rb +23 -1
- metadata +61 -13
- data/app/assets/config/manifest.js +0 -2
- data/app/assets/stylesheets/actiontext.scss +0 -36
- data/app/assets/stylesheets/application.css +0 -15
- data/app/views/madmin/application/_menu_resources.html.erb +0 -7
@@ -0,0 +1,80 @@
|
|
1
|
+
main {
|
2
|
+
padding-top: 1rem;
|
3
|
+
padding-right: 1rem;
|
4
|
+
padding-bottom: 1rem;
|
5
|
+
padding-left: calc(1rem + var(--sidebar-width));
|
6
|
+
}
|
7
|
+
|
8
|
+
#sidebar {
|
9
|
+
border-right: 1px solid var(--border-color);
|
10
|
+
height: 100%;
|
11
|
+
margin: 0;
|
12
|
+
overflow: auto;
|
13
|
+
padding: 1rem;
|
14
|
+
position: fixed;
|
15
|
+
width: var(--sidebar-width);
|
16
|
+
|
17
|
+
h1 {
|
18
|
+
margin-top: 0;
|
19
|
+
|
20
|
+
a {
|
21
|
+
color: var(--text-color);
|
22
|
+
text-decoration: none;
|
23
|
+
|
24
|
+
&:hover {
|
25
|
+
text-decoration: underline;
|
26
|
+
}
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
nav {
|
31
|
+
h4 {
|
32
|
+
margin-top: 0.5rem;
|
33
|
+
margin-bottom: 0.25rem;
|
34
|
+
}
|
35
|
+
|
36
|
+
a {
|
37
|
+
border-radius: .375rem;
|
38
|
+
color: var(--text-color);
|
39
|
+
display: block;
|
40
|
+
font-weight: 500;
|
41
|
+
padding: 0.5rem;
|
42
|
+
text-decoration: none;
|
43
|
+
|
44
|
+
margin-top: 0.1rem;
|
45
|
+
margin-bottom: 0.1rem;
|
46
|
+
|
47
|
+
&:hover {
|
48
|
+
background-color: rgb(243 244 246);
|
49
|
+
}
|
50
|
+
|
51
|
+
&.active {
|
52
|
+
background-color: rgb(243 244 246);
|
53
|
+
font-weight: 600;
|
54
|
+
}
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|
58
|
+
footer {
|
59
|
+
border-top: 1px solid var(--border-color);
|
60
|
+
padding-top: 1rem;
|
61
|
+
margin-top: 1rem;
|
62
|
+
|
63
|
+
a {
|
64
|
+
color: rgb(75 85 99);
|
65
|
+
display: flex;
|
66
|
+
align-items: center;
|
67
|
+
gap: 0.25rem;
|
68
|
+
padding: 0.5rem;
|
69
|
+
text-decoration: none;
|
70
|
+
|
71
|
+
svg {
|
72
|
+
display: inline-block;
|
73
|
+
}
|
74
|
+
}
|
75
|
+
|
76
|
+
&:hover {
|
77
|
+
color: var(--primary-color);
|
78
|
+
}
|
79
|
+
}
|
80
|
+
}
|
@@ -0,0 +1,56 @@
|
|
1
|
+
.table-scroll {
|
2
|
+
border-radius: 6px;
|
3
|
+
border: 1px solid var(--border-color);
|
4
|
+
overflow-x: auto;
|
5
|
+
position: relative;
|
6
|
+
}
|
7
|
+
|
8
|
+
table {
|
9
|
+
border-collapse: collapse;
|
10
|
+
min-width: 100%;
|
11
|
+
|
12
|
+
th {
|
13
|
+
background-color: var(--background-color);
|
14
|
+
border-bottom: 1px solid var(--border-color);
|
15
|
+
text-align: left;
|
16
|
+
padding-bottom: 0.75rem;
|
17
|
+
padding-top: 0.75rem;
|
18
|
+
padding-right: 0.875rem;
|
19
|
+
padding-left: 0.875rem;
|
20
|
+
|
21
|
+
a {
|
22
|
+
color: rgb(17 24 39);
|
23
|
+
text-decoration: none;
|
24
|
+
}
|
25
|
+
|
26
|
+
svg {
|
27
|
+
display: inline-block;
|
28
|
+
vertical-align: middle;
|
29
|
+
}
|
30
|
+
|
31
|
+
&.label {
|
32
|
+
background-color: var(--background-color);
|
33
|
+
border-right: 1px solid var(--border-color);
|
34
|
+
width: 16rem;
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
td {
|
39
|
+
padding-bottom: 0.75rem;
|
40
|
+
padding-top: 0.75rem;
|
41
|
+
padding-right: 0.875rem;
|
42
|
+
padding-left: 0.875rem;
|
43
|
+
|
44
|
+
a {
|
45
|
+
font-weight: 500;
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
tr {
|
50
|
+
border-bottom: 1px solid var(--border-color);
|
51
|
+
|
52
|
+
&:last-child {
|
53
|
+
border-bottom: none;
|
54
|
+
}
|
55
|
+
}
|
56
|
+
}
|
@@ -1,22 +1,17 @@
|
|
1
1
|
module Madmin
|
2
2
|
class ApplicationController < Madmin::BaseController
|
3
|
+
include Rails.application.routes.url_helpers
|
4
|
+
|
3
5
|
before_action :authenticate_admin_user
|
4
6
|
|
5
7
|
def authenticate_admin_user
|
6
8
|
# TODO: Add your authentication logic here
|
7
9
|
|
8
|
-
# For example,
|
9
|
-
# redirect_to "/", alert: "Not authorized." unless
|
10
|
-
end
|
11
|
-
|
12
|
-
# Authenticate with Clearance
|
13
|
-
# include Clearance::Controller
|
14
|
-
# before_action :require_login
|
10
|
+
# For example, with Rails 8 authentication
|
11
|
+
# redirect_to "/", alert: "Not authorized." unless authenticated? && Current.user.admin?
|
15
12
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
# Authenticate with Basic Auth
|
20
|
-
# http_basic_authenticate_with(name: Rails.application.credentials.admin_username, password: Rails.application.credentials.admin_password)
|
13
|
+
# Or with Devise
|
14
|
+
# redirect_to "/", alert: "Not authorized." unless current_user&.admin?
|
15
|
+
end
|
21
16
|
end
|
22
17
|
end
|
@@ -25,7 +25,7 @@ module Madmin
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def new
|
28
|
-
@record = resource.model.new
|
28
|
+
@record = resource.model.new(new_resource_params)
|
29
29
|
end
|
30
30
|
|
31
31
|
def create
|
@@ -85,6 +85,12 @@ module Madmin
|
|
85
85
|
.transform_values { |v| change_polymorphic(v) }
|
86
86
|
end
|
87
87
|
|
88
|
+
def new_resource_params
|
89
|
+
params.fetch(resource.param_key, {}).permit!
|
90
|
+
.permit(*resource.permitted_params)
|
91
|
+
.transform_values { |v| change_polymorphic(v) }
|
92
|
+
end
|
93
|
+
|
88
94
|
def change_polymorphic(data)
|
89
95
|
return data unless data.is_a?(ActionController::Parameters) && data[:type]
|
90
96
|
|
@@ -1,18 +1,7 @@
|
|
1
1
|
module Madmin
|
2
2
|
module ApplicationHelper
|
3
3
|
include Pagy::Frontend
|
4
|
-
|
5
|
-
# Converts a Rails version to a NPM version
|
6
|
-
def npm_rails_version
|
7
|
-
version = [
|
8
|
-
Rails::VERSION::MAJOR,
|
9
|
-
Rails::VERSION::MINOR,
|
10
|
-
Rails::VERSION::TINY
|
11
|
-
].join(".")
|
12
|
-
|
13
|
-
version += "-#{Rails::VERSION::PRE}" if Rails::VERSION::PRE
|
14
|
-
version
|
15
|
-
end
|
4
|
+
include Rails.application.routes.url_helpers
|
16
5
|
|
17
6
|
def clear_search_params
|
18
7
|
resource.index_path(sort: params[:sort], direction: params[:direction])
|
@@ -8,7 +8,7 @@ module Madmin
|
|
8
8
|
concat title
|
9
9
|
if matching_column
|
10
10
|
concat " "
|
11
|
-
concat tag.
|
11
|
+
concat tag.span((sort_direction == "asc") ? asc_icon : desc_icon)
|
12
12
|
end
|
13
13
|
end
|
14
14
|
end
|
@@ -28,5 +28,21 @@ module Madmin
|
|
28
28
|
def default_sort_direction
|
29
29
|
resource.try(:default_sort_direction) || "desc"
|
30
30
|
end
|
31
|
+
|
32
|
+
def asc_icon
|
33
|
+
<<~SVG.html_safe
|
34
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" height="1rem" width="1rem">
|
35
|
+
<path fill-rule="evenodd" d="M11.78 9.78a.75.75 0 0 1-1.06 0L8 7.06 5.28 9.78a.75.75 0 0 1-1.06-1.06l3.25-3.25a.75.75 0 0 1 1.06 0l3.25 3.25a.75.75 0 0 1 0 1.06Z" clip-rule="evenodd" />
|
36
|
+
</svg>
|
37
|
+
SVG
|
38
|
+
end
|
39
|
+
|
40
|
+
def desc_icon
|
41
|
+
<<~SVG.html_safe
|
42
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" height="1rem" width="1rem">
|
43
|
+
<path fill-rule="evenodd" d="M4.22 6.22a.75.75 0 0 1 1.06 0L8 8.94l2.72-2.72a.75.75 0 1 1 1.06 1.06l-3.25 3.25a.75.75 0 0 1-1.06 0L4.22 7.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" />
|
44
|
+
</svg>
|
45
|
+
SVG
|
46
|
+
end
|
31
47
|
end
|
32
48
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import { Application } from "@hotwired/stimulus"
|
2
|
+
|
3
|
+
const application = Application.start()
|
4
|
+
|
5
|
+
// Configure Stimulus development experience
|
6
|
+
application.debug = false
|
7
|
+
window.Stimulus = application
|
8
|
+
|
9
|
+
export { application }
|
10
|
+
|
11
|
+
import { Dropdown } from "tailwindcss-stimulus-components"
|
12
|
+
application.register("dropdown", Dropdown)
|
@@ -0,0 +1,5 @@
|
|
1
|
+
import { application } from "controllers/application"
|
2
|
+
|
3
|
+
// Eager load all controllers defined in the import map under controllers/**/*_controller
|
4
|
+
import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading"
|
5
|
+
eagerLoadControllersFrom("controllers", application)
|
@@ -0,0 +1,34 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static get targets() {
|
5
|
+
return [ "links", "template" ]
|
6
|
+
}
|
7
|
+
|
8
|
+
connect() {
|
9
|
+
this.wrapperClass = this.data.get("wrapperClass") || "nested-fields"
|
10
|
+
}
|
11
|
+
|
12
|
+
add_association(event) {
|
13
|
+
event.preventDefault()
|
14
|
+
|
15
|
+
var content = this.templateTarget.innerHTML.replace(/NEW_RECORD/g, new Date().getTime())
|
16
|
+
this.linksTarget.insertAdjacentHTML('beforebegin', content)
|
17
|
+
}
|
18
|
+
|
19
|
+
remove_association(event) {
|
20
|
+
event.preventDefault()
|
21
|
+
|
22
|
+
let wrapper = event.target.closest("." + this.wrapperClass)
|
23
|
+
|
24
|
+
// New records are simply removed from the page
|
25
|
+
if (wrapper.dataset.newRecord == "true") {
|
26
|
+
wrapper.remove()
|
27
|
+
|
28
|
+
// Existing records are hidden and flagged for deletion
|
29
|
+
} else {
|
30
|
+
wrapper.querySelector("input[name*='_destroy']").value = 1
|
31
|
+
wrapper.style.display = 'none'
|
32
|
+
}
|
33
|
+
}
|
34
|
+
}
|
@@ -0,0 +1,32 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
import TomSelect from "tom-select"
|
3
|
+
|
4
|
+
export default class extends Controller {
|
5
|
+
static values = {
|
6
|
+
options: Object,
|
7
|
+
url: String
|
8
|
+
}
|
9
|
+
|
10
|
+
connect() {
|
11
|
+
this.select = new TomSelect(this.element, {
|
12
|
+
plugins: ['remove_button'],
|
13
|
+
valueField: 'id',
|
14
|
+
labelField: 'name',
|
15
|
+
searchField: 'name',
|
16
|
+
load: (search, callback) => {
|
17
|
+
let url = search ? `${this.urlValue}?q=${search}` : this.urlValue;
|
18
|
+
fetch(url)
|
19
|
+
.then(response => response.json())
|
20
|
+
.then(json => {
|
21
|
+
callback(json);
|
22
|
+
}).catch(() => {
|
23
|
+
callback();
|
24
|
+
});
|
25
|
+
}
|
26
|
+
})
|
27
|
+
}
|
28
|
+
|
29
|
+
disconnect() {
|
30
|
+
this.select.destroy()
|
31
|
+
}
|
32
|
+
}
|
@@ -5,22 +5,21 @@
|
|
5
5
|
<meta name="ROBOTS" content="NOODP">
|
6
6
|
<meta name="viewport" content="initial-scale=1">
|
7
7
|
<title>
|
8
|
-
|
8
|
+
<% if content_for? :title %>
|
9
|
+
<%= yield(:title) %> -
|
10
|
+
<% end %>
|
11
|
+
<%= Madmin.site_name %> Admin
|
9
12
|
</title>
|
10
|
-
|
11
13
|
<%= csrf_meta_tags %>
|
12
|
-
|
13
14
|
<%= render "javascript" %>
|
14
15
|
</head>
|
15
|
-
<body
|
16
|
-
<
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
</main>
|
24
|
-
</div>
|
16
|
+
<body>
|
17
|
+
<aside id="sidebar">
|
18
|
+
<%= render "navigation" %>
|
19
|
+
</aside>
|
20
|
+
<main>
|
21
|
+
<%= render "flash" %>
|
22
|
+
<%= yield %>
|
23
|
+
</main>
|
25
24
|
</body>
|
26
25
|
</html>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<% if alert %>
|
2
|
+
<div class="alert alert-danger">
|
3
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor"><path fill-rule="evenodd" d="M8 15A7 7 0 1 0 8 1a7 7 0 0 0 0 14ZM8 4a.75.75 0 0 1 .75.75v3a.75.75 0 0 1-1.5 0v-3A.75.75 0 0 1 8 4Zm0 8a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z" clip-rule="evenodd"></path></svg>
|
4
|
+
<%= alert %>
|
5
|
+
</div>
|
6
|
+
<% end %>
|
7
|
+
|
8
|
+
<% if notice %>
|
9
|
+
<div class="alert alert-notice">
|
10
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor"><path fill-rule="evenodd" d="M8 15A7 7 0 1 0 8 1a7 7 0 0 0 0 14Zm3.844-8.791a.75.75 0 0 0-1.188-.918l-3.7 4.79-1.649-1.833a.75.75 0 1 0-1.114 1.004l2.25 2.5a.75.75 0 0 0 1.15-.043l4.25-5.5Z" clip-rule="evenodd"></path></svg>
|
11
|
+
<%= notice %>
|
12
|
+
</div>
|
13
|
+
<% end %>
|
@@ -1,23 +1,24 @@
|
|
1
1
|
<%= form_with model: [:madmin, record], url: (record.persisted? ? resource.show_path(record) : resource.index_path), local: true do |form| %>
|
2
2
|
<% if form.object.errors.any? %>
|
3
|
-
<div class="
|
4
|
-
<div class="
|
3
|
+
<div class="alert alert-danger">
|
4
|
+
<div class="">There was <%= pluralize form.object.errors.full_messages.count, "error" %> with your submission:</div>
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
<ul>
|
7
|
+
<% form.object.errors.full_messages.each do |message| %>
|
8
|
+
<li><%= message %></li>
|
9
|
+
<% end %>
|
10
|
+
</ul>
|
9
11
|
</div>
|
10
12
|
<% end %>
|
11
13
|
|
12
|
-
<% resource.attributes.values.each do |attribute| %>
|
13
|
-
|
14
|
-
|
15
|
-
<% next unless attribute.field.visible?(:form) %>
|
16
|
-
|
17
|
-
<div class="mb-4 md:flex">
|
14
|
+
<% resource.attributes.values.select{ _1.field.present? && _1.field.visible?(action_name) }.each do |attribute| %>
|
15
|
+
<div class="form-group">
|
16
|
+
<%= render "madmin/shared/label", form: form, field: attribute.field %>
|
18
17
|
<%= render partial: attribute.field.to_partial_path("form"), locals: { field: attribute.field, record: record, form: form, resource: resource } %>
|
18
|
+
<%= tag.div attribute.field.options.description, class: "form-description" if attribute.field.options.description.present? %>
|
19
19
|
</div>
|
20
20
|
<% end %>
|
21
21
|
|
22
|
-
<%= form.submit class: "
|
22
|
+
<%= form.submit class: "btn btn-primary" %>
|
23
|
+
<%= link_to "Cancel", (record.persisted? ? resource.show_path(record) : resource.index_path), class: "btn" %>
|
23
24
|
<% end %>
|
@@ -1,138 +1,5 @@
|
|
1
|
-
|
1
|
+
<%= javascript_importmap_tags "application", importmap: Madmin.importmap %>
|
2
|
+
|
3
|
+
<%= stylesheet_link_tag *Madmin.stylesheets, "data-turbo-track": "reload" %>
|
2
4
|
<%= stylesheet_link_tag "https://unpkg.com/flatpickr/dist/flatpickr.min.css", "data-turbo-track": "reload" %>
|
3
|
-
<%= stylesheet_link_tag "https://unpkg.com/trix/dist/trix.css", "data-turbo-track": "reload" %>
|
4
5
|
<%= stylesheet_link_tag "https://unpkg.com/tom-select/dist/css/tom-select.min.css", "data-turbo-track": "reload" %>
|
5
|
-
<style type="text/tailwindcss">
|
6
|
-
.pagy {
|
7
|
-
@apply isolate inline-flex rounded-md;
|
8
|
-
|
9
|
-
a:first-child {
|
10
|
-
@apply rounded-l-md;
|
11
|
-
}
|
12
|
-
a:last-child {
|
13
|
-
@apply rounded-r-md;
|
14
|
-
}
|
15
|
-
|
16
|
-
a {
|
17
|
-
@apply relative -ml-px inline-flex items-center bg-white px-3 py-2 text-sm font-semibold text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-100 focus:z-10;
|
18
|
-
|
19
|
-
&:not([href]) {
|
20
|
-
@apply text-gray-300 cursor-default;
|
21
|
-
}
|
22
|
-
|
23
|
-
&.current {
|
24
|
-
@apply text-white bg-blue-500 ring-blue-500;
|
25
|
-
}
|
26
|
-
}
|
27
|
-
|
28
|
-
label {
|
29
|
-
@apply inline-block whitespace-nowrap bg-gray-200 rounded-lg px-3 py-0.5;
|
30
|
-
input {
|
31
|
-
@apply bg-gray-100 border-none rounded-md;
|
32
|
-
}
|
33
|
-
}
|
34
|
-
}
|
35
|
-
</style>
|
36
|
-
|
37
|
-
<script type="importmap" data-turbo-track="reload">
|
38
|
-
{
|
39
|
-
"imports": {
|
40
|
-
"@hotwired/stimulus": "https://unpkg.com/@hotwired/stimulus/dist/stimulus.js",
|
41
|
-
"@hotwired/turbo": "https://unpkg.com/@hotwired/turbo",
|
42
|
-
"@hotwired/turbo-rails": "https://unpkg.com/@hotwired/turbo-rails",
|
43
|
-
"@rails/actiontext": "https://unpkg.com/@rails/actiontext@<%= npm_rails_version %>/app/assets/javascripts/actiontext.js",
|
44
|
-
"@rails/activestorage": "https://unpkg.com/@rails/activestorage@<%= npm_rails_version %>/app/assets/javascripts/activestorage.esm.js",
|
45
|
-
"flatpickr": "https://unpkg.com/flatpickr/dist/esm/index.js",
|
46
|
-
"stimulus-flatpickr": "https://unpkg.com/stimulus-flatpickr@3.0.0-0/dist/index.m.js",
|
47
|
-
"tailwindcss-stimulus-components": "https://unpkg.com/tailwindcss-stimulus-components/dist/tailwindcss-stimulus-components.module.js",
|
48
|
-
"tom-select": "https://unpkg.com/tom-select/dist/esm/tom-select.complete.js",
|
49
|
-
"trix": "https://unpkg.com/trix"
|
50
|
-
}
|
51
|
-
}
|
52
|
-
</script>
|
53
|
-
<script async src="https://unpkg.com/es-module-shims/dist/es-module-shims.js"></script>
|
54
|
-
|
55
|
-
<script type="module">
|
56
|
-
import * as Turbo from "@hotwired/turbo-rails"
|
57
|
-
|
58
|
-
import * as ActiveStorage from "@rails/activestorage"
|
59
|
-
ActiveStorage.start()
|
60
|
-
import "trix"
|
61
|
-
import "@rails/actiontext"
|
62
|
-
|
63
|
-
import { Application, Controller } from '@hotwired/stimulus'
|
64
|
-
const application = Application.start()
|
65
|
-
|
66
|
-
import { Dropdown } from "tailwindcss-stimulus-components"
|
67
|
-
application.register("dropdown", Dropdown)
|
68
|
-
|
69
|
-
import StimulusFlatpickr from "stimulus-flatpickr"
|
70
|
-
application.register("flatpickr", StimulusFlatpickr)
|
71
|
-
|
72
|
-
import TomSelect from "tom-select"
|
73
|
-
|
74
|
-
(() => {
|
75
|
-
application.register('select', class extends Controller {
|
76
|
-
static values = {
|
77
|
-
options: Object,
|
78
|
-
url: String
|
79
|
-
}
|
80
|
-
|
81
|
-
connect() {
|
82
|
-
this.select = new TomSelect(this.element, {
|
83
|
-
plugins: ['remove_button'],
|
84
|
-
valueField: 'id',
|
85
|
-
labelField: 'name',
|
86
|
-
searchField: 'name',
|
87
|
-
load: (search, callback) => {
|
88
|
-
let url = search ? `${this.urlValue}?q=${search}` : this.urlValue;
|
89
|
-
fetch(url)
|
90
|
-
.then(response => response.json())
|
91
|
-
.then(json => {
|
92
|
-
callback(json);
|
93
|
-
}).catch(() => {
|
94
|
-
callback();
|
95
|
-
});
|
96
|
-
}
|
97
|
-
})
|
98
|
-
}
|
99
|
-
|
100
|
-
disconnect() {
|
101
|
-
this.select.destroy()
|
102
|
-
}
|
103
|
-
})
|
104
|
-
|
105
|
-
application.register('nested-form', class extends Controller {
|
106
|
-
static get targets() {
|
107
|
-
return [ "links", "template" ]
|
108
|
-
}
|
109
|
-
|
110
|
-
connect() {
|
111
|
-
this.wrapperClass = this.data.get("wrapperClass") || "nested-fields"
|
112
|
-
}
|
113
|
-
|
114
|
-
add_association(event) {
|
115
|
-
event.preventDefault()
|
116
|
-
|
117
|
-
var content = this.templateTarget.innerHTML.replace(/NEW_RECORD/g, new Date().getTime())
|
118
|
-
this.linksTarget.insertAdjacentHTML('beforebegin', content)
|
119
|
-
}
|
120
|
-
|
121
|
-
remove_association(event) {
|
122
|
-
event.preventDefault()
|
123
|
-
|
124
|
-
let wrapper = event.target.closest("." + this.wrapperClass)
|
125
|
-
|
126
|
-
// New records are simply removed from the page
|
127
|
-
if (wrapper.dataset.newRecord == "true") {
|
128
|
-
wrapper.remove()
|
129
|
-
|
130
|
-
// Existing records are hidden and flagged for deletion
|
131
|
-
} else {
|
132
|
-
wrapper.querySelector("input[name*='_destroy']").value = 1
|
133
|
-
wrapper.style.display = 'none'
|
134
|
-
}
|
135
|
-
}
|
136
|
-
})
|
137
|
-
})()
|
138
|
-
</script>
|
@@ -1,29 +1,24 @@
|
|
1
|
-
<
|
2
|
-
<div class="flex md:block justify-between items-center">
|
3
|
-
<div class="flex md:block items-center">
|
4
|
-
<h1 class="mr-2 md:p-2 text-xl font-semibold">Madmin</h1>
|
5
|
-
<% if main_app.respond_to?(:root_url) %>
|
6
|
-
<%= link_to main_app.root_url, class: "block p-2 rounded hover:bg-gray-200", data: { turbo: false } do %>
|
7
|
-
← Back <span class="hidden md:inline">to App</span>
|
8
|
-
<% end %>
|
9
|
-
<% end %>
|
10
|
-
</div>
|
1
|
+
<h1><%= link_to_if respond_to?(:root_url), Madmin.site_name, root_url, data: {turbo: false} %></h1>
|
11
2
|
|
12
|
-
|
13
|
-
|
14
|
-
<span class="sr-only">Open main menu</span>
|
15
|
-
<!-- Heroicon name: outline/menu -->
|
16
|
-
<svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
17
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
|
18
|
-
</svg>
|
19
|
-
</button>
|
3
|
+
<nav>
|
4
|
+
<%= nav_link_to "Dashboard", madmin_root_path %>
|
20
5
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
6
|
+
<% Madmin.menu.render do |item| %>
|
7
|
+
<% if item.url %>
|
8
|
+
<%= nav_link_to item.label, item.url, starts_with: item.url %>
|
9
|
+
<% else %>
|
10
|
+
<h4><%= item.label %></h4>
|
11
|
+
<% end %>
|
12
|
+
|
13
|
+
<% item.items.each do |item| %>
|
14
|
+
<%= nav_link_to item.label, item.url, starts_with: item.url %>
|
15
|
+
<% end %>
|
16
|
+
<% end %>
|
17
|
+
</nav>
|
18
|
+
|
19
|
+
<footer>
|
20
|
+
<%= link_to "https://github.com/excid3/madmin", target: :_blank do %>
|
21
|
+
<svg viewBox="0 0 16 16" height="1rem" width="1rem" fill="currentColor" aria-hidden="true"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path></svg>
|
22
|
+
Madmin on GitHub
|
23
|
+
<% end %>
|
24
|
+
</footer>
|
@@ -1,7 +1,11 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
<
|
5
|
-
|
1
|
+
<%= content_for :title, "New #{resource.display_name(@record)}" %>
|
2
|
+
|
3
|
+
<header class="header">
|
4
|
+
<h1>
|
5
|
+
<%= link_to resource.friendly_name.pluralize, resource.index_path %>
|
6
|
+
/
|
7
|
+
<strong>Edit <%= link_to resource.display_name(@record), resource.show_path(@record) %></strong>
|
8
|
+
</h1>
|
9
|
+
</header>
|
6
10
|
|
7
11
|
<%= render partial: "form", locals: { record: @record, resource: resource } %>
|