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.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +36 -7
  3. data/app/assets/config/madmin_manifest.js +3 -0
  4. data/app/assets/stylesheets/madmin/actiontext.css +31 -0
  5. data/app/assets/stylesheets/madmin/application-sprockets.css +11 -0
  6. data/app/assets/stylesheets/madmin/application.css +10 -0
  7. data/app/assets/stylesheets/madmin/base.css +117 -0
  8. data/app/assets/stylesheets/madmin/buttons.css +46 -0
  9. data/app/assets/stylesheets/madmin/forms.css +64 -0
  10. data/app/assets/stylesheets/madmin/pagination.css +59 -0
  11. data/app/assets/stylesheets/madmin/reset.css +242 -0
  12. data/app/assets/stylesheets/madmin/sidebar.css +80 -0
  13. data/app/assets/stylesheets/madmin/tables.css +56 -0
  14. data/app/controllers/madmin/application_controller.rb +7 -12
  15. data/app/controllers/madmin/base_controller.rb +1 -0
  16. data/app/controllers/madmin/resource_controller.rb +7 -1
  17. data/app/helpers/madmin/application_helper.rb +1 -12
  18. data/app/helpers/madmin/sort_helper.rb +17 -1
  19. data/app/javascript/madmin/application.js +4 -0
  20. data/app/javascript/madmin/controllers/application.js +12 -0
  21. data/app/javascript/madmin/controllers/index.js +5 -0
  22. data/app/javascript/madmin/controllers/nested_form_controller.js +34 -0
  23. data/app/javascript/madmin/controllers/select_controller.js +32 -0
  24. data/app/views/layouts/madmin/application.html.erb +12 -13
  25. data/app/views/madmin/application/_flash.html.erb +13 -0
  26. data/app/views/madmin/application/_form.html.erb +13 -12
  27. data/app/views/madmin/application/_javascript.html.erb +3 -136
  28. data/app/views/madmin/application/_navigation.html.erb +22 -27
  29. data/app/views/madmin/application/edit.html.erb +9 -5
  30. data/app/views/madmin/application/index.html.erb +37 -31
  31. data/app/views/madmin/application/new.html.erb +9 -5
  32. data/app/views/madmin/application/show.html.erb +28 -22
  33. data/app/views/madmin/dashboard/show.html.erb +4 -1
  34. data/app/views/madmin/fields/attachment/_form.html.erb +11 -4
  35. data/app/views/madmin/fields/attachment/_index.html.erb +5 -1
  36. data/app/views/madmin/fields/attachment/_show.html.erb +4 -4
  37. data/app/views/madmin/fields/attachments/_form.html.erb +1 -4
  38. data/app/views/madmin/fields/belongs_to/_form.html.erb +1 -4
  39. data/app/views/madmin/fields/belongs_to/_index.html.erb +2 -1
  40. data/app/views/madmin/fields/boolean/_form.html.erb +3 -4
  41. data/app/views/madmin/fields/currency/_form.html.erb +1 -0
  42. data/app/views/madmin/fields/currency/_index.html.erb +1 -0
  43. data/app/views/madmin/fields/currency/_show.html.erb +1 -0
  44. data/app/views/madmin/fields/date/_form.html.erb +1 -4
  45. data/app/views/madmin/fields/date_time/_form.html.erb +1 -4
  46. data/app/views/madmin/fields/decimal/_form.html.erb +1 -4
  47. data/app/views/madmin/fields/enum/_form.html.erb +1 -4
  48. data/app/views/madmin/fields/file/_form.html.erb +1 -4
  49. data/app/views/madmin/fields/float/_form.html.erb +1 -4
  50. data/app/views/madmin/fields/has_many/_form.html.erb +1 -4
  51. data/app/views/madmin/fields/has_many/_show.html.erb +7 -1
  52. data/app/views/madmin/fields/has_one/_form.html.erb +0 -3
  53. data/app/views/madmin/fields/integer/_form.html.erb +1 -4
  54. data/app/views/madmin/fields/integer/_index.html.erb +1 -5
  55. data/app/views/madmin/fields/json/_form.html.erb +1 -4
  56. data/app/views/madmin/fields/nested_has_many/_fields.html.erb +4 -5
  57. data/app/views/madmin/fields/nested_has_many/_form.html.erb +0 -4
  58. data/app/views/madmin/fields/nested_has_many/_show.html.erb +7 -1
  59. data/app/views/madmin/fields/password/_form.html.erb +1 -4
  60. data/app/views/madmin/fields/polymorphic/_form.html.erb +1 -4
  61. data/app/views/madmin/fields/rich_text/_form.html.erb +1 -6
  62. data/app/views/madmin/fields/select/_form.html.erb +1 -0
  63. data/app/views/madmin/fields/select/_index.html.erb +1 -0
  64. data/app/views/madmin/fields/select/_show.html.erb +1 -0
  65. data/app/views/madmin/fields/string/_form.html.erb +1 -4
  66. data/app/views/madmin/fields/text/_form.html.erb +1 -4
  67. data/app/views/madmin/shared/_label.html.erb +2 -2
  68. data/config/importmap.rb +10 -0
  69. data/lib/generators/madmin/field/templates/_form.html.erb +1 -4
  70. data/lib/generators/madmin/install/templates/controller.rb.tt +5 -12
  71. data/lib/generators/madmin/resource/resource_generator.rb +1 -1
  72. data/lib/generators/madmin/resource/templates/resource.rb.tt +12 -10
  73. data/lib/madmin/engine.rb +26 -0
  74. data/lib/madmin/field.rb +21 -5
  75. data/lib/madmin/fields/belongs_to.rb +10 -5
  76. data/lib/madmin/fields/currency.rb +15 -0
  77. data/lib/madmin/fields/has_many.rb +17 -0
  78. data/lib/madmin/fields/nested_has_many.rb +5 -1
  79. data/lib/madmin/fields/polymorphic.rb +1 -1
  80. data/lib/madmin/fields/select.rb +9 -0
  81. data/lib/madmin/generator_helpers.rb +6 -2
  82. data/lib/madmin/menu.rb +70 -0
  83. data/lib/madmin/resource.rb +56 -24
  84. data/lib/madmin/search.rb +1 -1
  85. data/lib/madmin/version.rb +1 -1
  86. data/lib/madmin.rb +23 -1
  87. metadata +61 -13
  88. data/app/assets/config/manifest.js +0 -2
  89. data/app/assets/stylesheets/actiontext.scss +0 -36
  90. data/app/assets/stylesheets/application.css +0 -15
  91. 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, we could redirect if the user isn't an admin
9
- # redirect_to "/", alert: "Not authorized." unless user_signed_in? && current_user.admin?
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
- # Authenticate with Devise
17
- # before_action :authenticate_user!
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
@@ -1,5 +1,6 @@
1
1
  module Madmin
2
2
  class BaseController < ActionController::Base
3
+ include ::ActiveStorage::SetCurrent if defined?(::ActiveStorage)
3
4
  include Pagy::Backend
4
5
 
5
6
  protect_from_forgery with: :exception
@@ -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.i((sort_direction == "asc") ? "▲" : "▼")
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,4 @@
1
+ import "@hotwired/turbo-rails"
2
+ import "trix"
3
+ import "@rails/actiontext"
4
+ import "controllers"
@@ -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
- Madmin: <%= Rails.application.class %>
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 class="min-h-screen">
16
- <div class="md:flex w-full min-h-screen">
17
- <div id="sidebar" class="md:w-64 p-4 flex-shrink-0 border-r">
18
- <%= render "navigation" -%>
19
- </div>
20
- <main class="flex-grow p-4 overflow-x-scroll" role="main">
21
- <%#= render "flashes" -%>
22
- <%= yield %>
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="mb-4 rounded-md text-sm text-red-700 bg-red-100 p-4">
4
- <div class="mb-2 font-medium leading-5 text-red-800">There was <%= pluralize form.object.errors.full_messages.count, "error" %> with your submission</div>
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
- <% form.object.errors.full_messages.each do |message| %>
7
- <div class="ml-4"><%= message %></div>
8
- <% end %>
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
- <% next if attribute.field.nil? %>
14
- <% next unless attribute.field.visible?(action_name) %>
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: "bg-white hover:bg-gray-100 text-gray-800 font-semibold py-2 px-4 border border-gray-400 rounded shadow" %>
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
- <script src="https://cdn.tailwindcss.com?plugins=forms,typography,aspect-ratio,line-clamp"></script>
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
- <div class="flex flex-col h-full text-sm">
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
- <div class="-mr-2 flex items-center md:hidden relative" data-controller="dropdown">
13
- <button data-action="click->dropdown#toggle touch->dropdown#toggle click@window->dropdown#hide touch@window#dropdown->hide" type="button" class="bg-white rounded-md p-2 inline-flex items-center justify-center text-gray-400 hover:bg-gray-200 focus:outline-none focus:ring-2 focus-ring-inset focus:ring-white" id="main-menu" aria-haspopup="true">
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
- <div class="absolute top-12 right-0 bg-white rounded-lg min-w-64 shadow-lg hidden md:flex flex-col flex-grow justify-between" data-dropdown-target="menu">
22
- <%= render "menu_resources" %>
23
- </div>
24
- </div>
25
- <div class="hidden md:block">
26
- <%= render "menu_resources" %>
27
- </div>
28
- </div>
29
- </div>
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
- <h1 class="text-xl mb-4">
2
- <%= link_to resource.friendly_name.pluralize, resource.index_path, class: "text-blue-500" %>
3
- /
4
- <strong>Edit <%= link_to resource.display_name(@record), resource.show_path(@record), class: "text-blue-500" %></strong>
5
- </h1>
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 } %>