madmin 1.2.10 → 2.0.0

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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/config/madmin_manifest.js +3 -0
  3. data/app/assets/stylesheets/madmin/actiontext.css +31 -0
  4. data/app/assets/stylesheets/madmin/application.css +88 -0
  5. data/app/assets/stylesheets/madmin/buttons.css +46 -0
  6. data/app/assets/stylesheets/madmin/forms.css +82 -0
  7. data/app/assets/stylesheets/madmin/pagination.css +59 -0
  8. data/app/assets/stylesheets/madmin/reset.css +242 -0
  9. data/app/assets/stylesheets/madmin/sidebar.css +80 -0
  10. data/app/assets/stylesheets/madmin/tables.css +56 -0
  11. data/app/controllers/madmin/application_controller.rb +7 -12
  12. data/app/controllers/madmin/base_controller.rb +1 -0
  13. data/app/helpers/madmin/application_helper.rb +1 -12
  14. data/app/helpers/madmin/sort_helper.rb +17 -1
  15. data/app/javascript/madmin/application.js +4 -0
  16. data/app/javascript/madmin/controllers/application.js +12 -0
  17. data/app/javascript/madmin/controllers/index.js +5 -0
  18. data/app/javascript/madmin/controllers/nested_form_controller.js +34 -0
  19. data/app/javascript/madmin/controllers/select_controller.js +32 -0
  20. data/app/views/layouts/madmin/application.html.erb +11 -13
  21. data/app/views/madmin/application/_form.html.erb +13 -12
  22. data/app/views/madmin/application/_javascript.html.erb +3 -136
  23. data/app/views/madmin/application/_navigation.html.erb +22 -27
  24. data/app/views/madmin/application/edit.html.erb +9 -5
  25. data/app/views/madmin/application/index.html.erb +37 -31
  26. data/app/views/madmin/application/new.html.erb +9 -5
  27. data/app/views/madmin/application/show.html.erb +28 -22
  28. data/app/views/madmin/dashboard/show.html.erb +4 -1
  29. data/app/views/madmin/fields/attachment/_form.html.erb +11 -4
  30. data/app/views/madmin/fields/attachment/_index.html.erb +5 -1
  31. data/app/views/madmin/fields/attachment/_show.html.erb +4 -4
  32. data/app/views/madmin/fields/attachments/_form.html.erb +1 -4
  33. data/app/views/madmin/fields/belongs_to/_form.html.erb +1 -4
  34. data/app/views/madmin/fields/belongs_to/_index.html.erb +2 -1
  35. data/app/views/madmin/fields/boolean/_form.html.erb +3 -4
  36. data/app/views/madmin/fields/currency/_form.html.erb +1 -0
  37. data/app/views/madmin/fields/currency/_index.html.erb +1 -0
  38. data/app/views/madmin/fields/currency/_show.html.erb +1 -0
  39. data/app/views/madmin/fields/date/_form.html.erb +1 -4
  40. data/app/views/madmin/fields/date_time/_form.html.erb +1 -4
  41. data/app/views/madmin/fields/decimal/_form.html.erb +0 -3
  42. data/app/views/madmin/fields/enum/_form.html.erb +1 -4
  43. data/app/views/madmin/fields/file/_form.html.erb +0 -3
  44. data/app/views/madmin/fields/float/_form.html.erb +0 -3
  45. data/app/views/madmin/fields/has_many/_form.html.erb +1 -4
  46. data/app/views/madmin/fields/has_one/_form.html.erb +0 -3
  47. data/app/views/madmin/fields/integer/_form.html.erb +0 -3
  48. data/app/views/madmin/fields/integer/_index.html.erb +1 -5
  49. data/app/views/madmin/fields/json/_form.html.erb +0 -3
  50. data/app/views/madmin/fields/nested_has_many/_fields.html.erb +4 -5
  51. data/app/views/madmin/fields/nested_has_many/_form.html.erb +0 -4
  52. data/app/views/madmin/fields/password/_form.html.erb +0 -3
  53. data/app/views/madmin/fields/polymorphic/_form.html.erb +1 -4
  54. data/app/views/madmin/fields/rich_text/_form.html.erb +1 -6
  55. data/app/views/madmin/fields/select/_form.html.erb +1 -0
  56. data/app/views/madmin/fields/select/_index.html.erb +1 -0
  57. data/app/views/madmin/fields/select/_show.html.erb +1 -0
  58. data/app/views/madmin/fields/string/_form.html.erb +1 -4
  59. data/app/views/madmin/fields/text/_form.html.erb +0 -3
  60. data/app/views/madmin/shared/_label.html.erb +2 -2
  61. data/config/importmap.rb +10 -0
  62. data/lib/generators/madmin/field/templates/_form.html.erb +0 -3
  63. data/lib/generators/madmin/install/templates/controller.rb.tt +5 -12
  64. data/lib/generators/madmin/resource/templates/resource.rb.tt +12 -10
  65. data/lib/madmin/engine.rb +20 -0
  66. data/lib/madmin/field.rb +17 -5
  67. data/lib/madmin/fields/belongs_to.rb +10 -5
  68. data/lib/madmin/fields/currency.rb +15 -0
  69. data/lib/madmin/fields/polymorphic.rb +1 -1
  70. data/lib/madmin/fields/select.rb +9 -0
  71. data/lib/madmin/menu.rb +70 -0
  72. data/lib/madmin/resource.rb +56 -24
  73. data/lib/madmin/search.rb +1 -1
  74. data/lib/madmin/version.rb +1 -1
  75. data/lib/madmin.rb +22 -1
  76. metadata +61 -13
  77. data/app/assets/config/manifest.js +0 -2
  78. data/app/assets/stylesheets/actiontext.scss +0 -36
  79. data/app/assets/stylesheets/application.css +0 -15
  80. data/app/views/madmin/application/_menu_resources.html.erb +0 -7
@@ -1,32 +1,38 @@
1
- <div class="md:flex items-center justify-between mb-4">
2
- <h1 class="text-xl">
3
- <%= link_to resource.friendly_name.pluralize, resource.index_path, class: "text-blue-500" %>
1
+ <%= content_for :title, resource.display_name(@record) %>
2
+
3
+ <header class="header">
4
+ <h1>
5
+ <%= link_to resource.friendly_name.pluralize, resource.index_path %>
4
6
  /
5
- <%= link_to resource.display_name(@record), resource.show_path(@record), class: "text-blue-500 font-bold" %>
7
+ <%= resource.display_name(@record) %>
6
8
  </h1>
7
9
 
8
- <div class="flex gap-2 items-center px-4">
10
+ <div class="actions">
9
11
  <% resource.member_actions.each do |action| %>
10
- <%= instance_eval(&action) %>
12
+ <%= instance_exec(@record, &action) %>
11
13
  <% end %>
12
- <%= link_to "Edit", resource.edit_path(@record), class: "block bg-white hover:bg-gray-100 text-gray-800 font-semibold py-2 px-4 border border-gray-400 rounded shadow" %>
13
- <%= button_to "Delete", resource.show_path(@record), method: :delete, data: { turbo_confirm: "Are you sure?" }, class: "bg-white hover:bg-gray-100 text-red-500 font-semibold py-2 px-4 border border-red-500 rounded shadow pointer-cursor" %>
14
+ <%= link_to "Edit", resource.edit_path(@record), class: "btn btn-secondary" %>
15
+ <%= button_to "Delete", resource.show_path(@record), method: :delete, data: { turbo_confirm: "Are you sure?" }, class: "btn btn-danger" %>
14
16
  </div>
15
- </div>
17
+ </header>
16
18
 
17
- <div class="divide-y">
18
- <% resource.attributes.values.each do |attribute| %>
19
- <% next if attribute.field.nil? %>
20
- <% next unless attribute.field.visible?(action_name) %>
19
+ <div class="table-scroll">
20
+ <table>
21
+ <tbody>
22
+ <% resource.attributes.values.each do |attribute| %>
23
+ <% next if attribute.field.nil? %>
24
+ <% next unless attribute.field.visible?(action_name) %>
21
25
 
22
- <div class="px-4 py-3 md:grid md:grid-cols-4 md:gap-4 md:px-6">
23
- <div class="text-sm font-medium text-gray-500">
24
- <%= attribute.field.attribute_name.to_s.titleize %>
25
- </div>
26
+ <tr>
27
+ <th class="label">
28
+ <%= attribute.field.options.label || attribute.name.to_s.titleize %>
29
+ </th>
26
30
 
27
- <div class="md:col-span-3">
28
- <%= render partial: attribute.field.to_partial_path("show"), locals: { field: attribute.field, record: @record, resource: resource } %>
29
- </div>
30
- </div>
31
- <% end %>
31
+ <td>
32
+ <%= render partial: attribute.field.to_partial_path("show"), locals: { field: attribute.field, record: @record, resource: resource } %>
33
+ </td>
34
+ </tr>
35
+ <% end %>
36
+ </tbody>
37
+ </table>
32
38
  </div>
@@ -1 +1,4 @@
1
- <h1>Madmin</h1>
1
+ <div class="prose">
2
+ <h1>Madmin Dashboard</h1>
3
+ <p>Create <code>app/views/madmin/dashboard/show.html.erb</code> to customize your dashboard.</p>
4
+ </div>
@@ -1,4 +1,11 @@
1
- <div class="block md:inline-block md:w-32 flex-shrink-0 text-gray-700">
2
- <%= render "madmin/shared/label", form: form, field: field %>
3
- </div>
4
- <%= form.file_field field.to_param %>
1
+ <%= form.file_field field.to_param, class: "form-input"%>
2
+
3
+ <% if form.object.persisted? %>
4
+ <div class="mt-2">
5
+ <%= render partial: field.to_partial_path("show"), locals: {field: field, record: record} %>
6
+
7
+ <% if (value = field.value(record) && value&.attached?) %>
8
+ <%= link_to "Remove", Madmin.resource_for(value).show_path(value), data: { turbo_method: :delete, turbo_confirm: "Are you sure? You will lose any other changes."} %>
9
+ <% end %>
10
+ </div>
11
+ <% end %>
@@ -1,3 +1,7 @@
1
1
  <% if (attachment = field.value(record)) && attachment.attached? %>
2
- <%= link_to attachment.filename, main_app.url_for(attachment), target: :_blank %>
2
+ <% if attachment.variable? %>
3
+ <%= image_tag main_app.url_for(attachment), class: "max-h-8" %>
4
+ <% else %>
5
+ <%= attachment.filename %>
6
+ <% end %>
3
7
  <% end %>
@@ -1,9 +1,9 @@
1
1
  <% if (attachment = field.value(record)) && attachment.attached? %>
2
2
  <% if attachment.variable? %>
3
- <%= link_to main_app.url_for(attachment), target: :_blank do %>
4
- <%= image_tag main_app.url_for(attachment), class: "max-h-32" %>
5
- <% end %>
3
+ <%= image_tag main_app.url_for(attachment), class: "max-h-32" %>
6
4
  <% else %>
7
5
  <%= link_to attachment.filename, main_app.url_for(attachment), target: :_blank, class: "text-blue-500 underline" %>
8
6
  <% end %>
9
- <% end %>
7
+
8
+ <%= link_to "Remove", Madmin.resource_for(attachment).show_path(attachment), data: { turbo_method: :delete, turbo_confirm: "Are you sure? You will lose any other changes."} %>
9
+ <% end %>
@@ -1,4 +1 @@
1
- <div class="block md:inline-block md:w-32 flex-shrink-0 text-gray-700">
2
- <%= render "madmin/shared/label", form: form, field: field %>
3
- </div>
4
- <%= form.file_field field.attribute_name, multiple: true %>
1
+ <%= form.file_field field.attribute_name, multiple: true, class: "form-input" %>
@@ -1,4 +1 @@
1
- <div class="block md:inline-block md:w-32 flex-shrink-0 text-gray-700">
2
- <%= render "madmin/shared/label", form: form, field: field %>
3
- </div>
4
- <%= form.select field.to_param, field.options_for_select(record), { prompt: true }, { class: "form-select", data: { controller: "select", select_url_value: field.index_path } } %>
1
+ <%= form.select field.to_param, field.options_for_select(record), { prompt: true }, { data: { controller: "select", select_url_value: field.index_path } } %>
@@ -1,3 +1,4 @@
1
1
  <% if (object = field.value(record)) %>
2
- <%= link_to Madmin.resource_for(object).display_name(object), Madmin.resource_for(object).show_path(object) %>
2
+ <% object_resource = Madmin.resource_for(object) %>
3
+ <%= link_to object_resource.display_name(object), object_resource.show_path(object) %>
3
4
  <% end %>
@@ -1,4 +1,3 @@
1
- <div class="block md:inline-block md:w-32 flex-shrink-0 text-gray-700">
2
- <%= render "madmin/shared/label", form: form, field: field %>
3
- </div>
4
- <%= form.check_box field.attribute_name, class: "form-input" %>
1
+ <div class="form-input">
2
+ <%= form.check_box field.attribute_name %>
3
+ </div>
@@ -0,0 +1 @@
1
+ <%= form.number_field field.attribute_name, class: "form-input" %>
@@ -0,0 +1 @@
1
+ <%= number_to_currency field.value(record) if field.value(record) %>
@@ -0,0 +1 @@
1
+ <%= number_to_currency field.value(record) if field.value(record) %>
@@ -1,4 +1 @@
1
- <div class="block md:inline-block md:w-32 flex-shrink-0 text-gray-700">
2
- <%= render "madmin/shared/label", form: form, field: field %>
3
- </div>
4
- <%= form.text_field field.attribute_name, { class: "form-select", data: { controller: "flatpickr" } } %>
1
+ <%= form.date_field field.attribute_name, class: "form-input" %>
@@ -1,4 +1 @@
1
- <div class="block md:inline-block md:w-32 flex-shrink-0 text-gray-700">
2
- <%= render "madmin/shared/label", form: form, field: field %>
3
- </div>
4
- <%= form.text_field field.attribute_name, data: { controller: "flatpickr", flatpickr_enable_time: true } %>
1
+ <%= form.datetime_field field.attribute_name, class: "form-input" %>
@@ -1,4 +1 @@
1
- <div class="block md:inline-block md:w-32 flex-shrink-0 text-gray-700">
2
- <%= render "madmin/shared/label", form: form, field: field %>
3
- </div>
4
1
  <%= form.number_field field.attribute_name, step: :any, class: "form-input" %>
@@ -1,4 +1 @@
1
- <div class="block md:inline-block md:w-32 flex-shrink-0 text-gray-700">
2
- <%= render "madmin/shared/label", form: form, field: field %>
3
- </div>
4
- <%= form.select field.attribute_name, field.options_for_select(record), { prompt: true }, { class: "form-select", data: { controller: "select" } } %>
1
+ <%= form.select field.attribute_name, field.options_for_select(record), { prompt: true }, { data: { controller: "select" } } %>
@@ -1,4 +1 @@
1
- <div class="block md:inline-block md:w-32 flex-shrink-0 text-gray-700">
2
- <%= render "madmin/shared/label", form: form, field: field %>
3
- </div>
4
1
  <%= form.file_field field.attribute_name, class: "form-input" %>
@@ -1,4 +1 @@
1
- <div class="block md:inline-block md:w-32 flex-shrink-0 text-gray-700">
2
- <%= render "madmin/shared/label", form: form, field: field %>
3
- </div>
4
1
  <%= form.number_field field.attribute_name, step: :any, class: "form-input" %>
@@ -1,4 +1 @@
1
- <div class="block md:inline-block md:w-32 flex-shrink-0 text-gray-700">
2
- <%= render "madmin/shared/label", form: form, field: field %>
3
- </div>
4
- <%= form.select "#{field.attribute_name.to_s.singularize}_ids", field.options_for_select(record), { prompt: true }, { multiple: true, class: "form-select", data: { controller: "select", select_url_value: field.index_path } } %>
1
+ <%= form.select "#{field.attribute_name.to_s.singularize}_ids", field.options_for_select(record), { prompt: true }, { multiple: true, data: { controller: "select", select_url_value: field.index_path } } %>
@@ -1,4 +1 @@
1
- <div class="block md:inline-block md:w-32 flex-shrink-0 text-gray-700">
2
- <%= render "madmin/shared/label", form: form, field: field %>
3
- </div>
4
1
  <%= field.value(record) %>
@@ -1,4 +1 @@
1
- <div class="block md:inline-block md:w-32 flex-shrink-0 text-gray-700">
2
- <%= render "madmin/shared/label", form: form, field: field %>
3
- </div>
4
1
  <%= form.number_field field.attribute_name, class: "form-input" %>
@@ -1,5 +1 @@
1
- <% if field.attribute_name == :id %>
2
- <%= link_to field.value(record), resource.show_path(record), class: "text-blue-500 underline" %>
3
- <% else %>
4
- <%= field.value(record) %>
5
- <% end %>
1
+ <%= link_to_if field.attribute_name.to_s == resource.model.primary_key, field.value(record), resource.show_path(record) %>
@@ -1,4 +1 @@
1
- <div class="block md:inline-block md:w-32 flex-shrink-0 text-gray-700">
2
- <%= render "madmin/shared/label", form: form, field: field %>
3
- </div>
4
1
  <%= form.text_area field.attribute_name, class: "form-input" %>
@@ -1,10 +1,9 @@
1
1
  <%= content_tag :div, class: "nested-fields border border-gray-200 rounded-lg p-5", data: { new_record: f.object.new_record? } do %>
2
2
  <% field.nested_attributes.each do |name, nested_attribute| %>
3
- <% next if nested_attribute[:field].nil? %>
4
- <% next unless nested_attribute[:field].visible?(action_name) %>
5
- <% next unless nested_attribute[:field].visible?(:form) %>
6
-
7
- <% nested_field = nested_attribute[:field] %>
3
+ <% nested_field = nested_attribute.field %>
4
+ <% next if nested_field.nil? %>
5
+ <% next unless nested_field.visible?(action_name) %>
6
+ <% next unless nested_field.visible?(:form) %>
8
7
 
9
8
  <div class="mb-4 flex">
10
9
  <%= render partial: nested_field.to_partial_path("form"), locals: { field: nested_field, record: f.object, form: f, resource: field.resource } %>
@@ -1,7 +1,3 @@
1
- <div class="block md:inline-block w-32 flex-shrink-0">
2
- <%= render "madmin/shared/label", form: form, field: field %>
3
- </div>
4
-
5
1
  <div class="container space-y-8" data-controller="nested-form">
6
2
  <template data-target="nested-form.template">
7
3
 
@@ -1,4 +1 @@
1
- <div class="block md:inline-block md:w-32 flex-shrink-0 text-gray-700">
2
- <%= render "madmin/shared/label", form: form, field: field %>
3
- </div>
4
1
  <%= form.password_field field.attribute_name, class: "form-input" %>
@@ -1,7 +1,4 @@
1
1
  <%= form.fields_for field.attribute_name do |pf| %>
2
- <div class="block md:inline-block md:w-32 flex-shrink-0 text-gray-700">
3
- <%= render "madmin/shared/label", form: pf, field: field %>
4
- </div>
5
- <%= pf.select :value, field.options_for_select(record).map(&:to_global_id), { selected: field.value(record)&.to_global_id, prompt: true }, { class: "form-select", data: { controller: "slimselect" } } %>
2
+ <%= pf.select :value, field.options_for_select(record).map(&:to_global_id), { selected: field.value(record)&.to_global_id, prompt: true }, { data: { controller: "select" } } %>
6
3
  <%= pf.hidden_field :type, value: "polymorphic" %>
7
4
  <% end %>
@@ -1,6 +1 @@
1
- <div class="block md:inline-block md:w-32 flex-shrink-0 text-gray-700">
2
- <%= render "madmin/shared/label", form: form, field: field %>
3
- </div>
4
- <div class="flex-1">
5
- <%= form.rich_text_area field.attribute_name, class: "form-input block" %>
6
- </div>
1
+ <%= form.rich_text_area field.attribute_name, class: "form-input" %>
@@ -0,0 +1 @@
1
+ <%= form.select field.attribute_name, field.options_for_select(record), { prompt: true }, { data: { controller: "select" } } %>
@@ -0,0 +1 @@
1
+ <%= field.value(record) %>
@@ -0,0 +1 @@
1
+ <%= field.value(record) %>
@@ -1,4 +1 @@
1
- <div class="block md:inline-block md:w-32 flex-shrink-0 text-gray-700">
2
- <%= render "madmin/shared/label", form: form, field: field %>
3
- </div>
4
- <%= form.text_field field.attribute_name, class: "form-input" %>
1
+ <%= form.text_field field.attribute_name, class: "form-input", placeholder: field.options.placeholder %>
@@ -1,4 +1 @@
1
- <div class="block md:inline-block md:w-32 flex-shrink-0 text-gray-700">
2
- <%= render "madmin/shared/label", form: form, field: field %>
3
- </div>
4
1
  <%= form.text_area field.attribute_name, class: "form-input" %>
@@ -1,4 +1,4 @@
1
- <%= form.label field.attribute_name %>
1
+ <%= form.label field.attribute_name, field.options.label %>
2
2
  <% if field.required? %>
3
- <span class="text-red-500">*</span>
3
+ <span class="required">*</span>
4
4
  <% end %>
@@ -0,0 +1,10 @@
1
+ pin "application", to: "madmin/application.js", preload: true
2
+ pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true
3
+ pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true
4
+ pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
5
+ pin "trix"
6
+ pin "@rails/actiontext", to: "actiontext.esm.js"
7
+ pin_all_from Madmin::Engine.root.join("app/javascript/madmin/controllers"), under: "controllers", to: "madmin/controllers"
8
+
9
+ pin "tom-select", to: "https://ga.jspm.io/npm:tom-select@2.4.1/dist/js/tom-select.complete.js"
10
+ pin "tailwindcss-stimulus-components", to: "https://ga.jspm.io/npm:tailwindcss-stimulus-components@6.1.2/dist/tailwindcss-stimulus-components.module.js"
@@ -1,4 +1 @@
1
- <div class="block md:inline-block md:w-32 flex-shrink-0 text-gray-700">
2
- <%= render "madmin/shared/label", form: form, field: field %>
3
- </div>
4
1
  <%= form.text_field field.attribute_name, class: "form-input" %>
@@ -5,18 +5,11 @@ module Madmin
5
5
  def authenticate_admin_user
6
6
  # TODO: Add your authentication logic here
7
7
 
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
8
+ # For example, with Rails 8 authentication
9
+ # redirect_to "/", alert: "Not authorized." unless authenticated? && Current.user.admin?
15
10
 
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)
11
+ # Or with Devise
12
+ # redirect_to "/", alert: "Not authorized." unless current_user&.admin?
13
+ end
21
14
  end
22
15
  end
@@ -9,17 +9,19 @@ class <%= class_name %>Resource < Madmin::Resource
9
9
  attribute :<%= association_name %>
10
10
  <% end -%>
11
11
 
12
- # Uncomment this to customize the display name of records in the admin area.
13
- # def self.display_name(record)
14
- # record.name
15
- # end
12
+ # Add scopes to easily filter records
13
+ # scope :published
16
14
 
17
- # Uncomment this to customize the default sort column and direction.
18
- # def self.default_sort_column
19
- # "created_at"
15
+ # Add actions to the resource's show page
16
+ # member_action do |record|
17
+ # link_to "Do Something", some_path
20
18
  # end
19
+
20
+ # Customize the display name of records in the admin area.
21
+ # def self.display_name(record) = record.name
22
+
23
+ # Customize the default sort column and direction.
24
+ # def self.default_sort_column = "created_at"
21
25
  #
22
- # def self.default_sort_direction
23
- # "desc"
24
- # end
26
+ # def self.default_sort_direction = "desc"
25
27
  end
data/lib/madmin/engine.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require "importmap-rails"
2
+
1
3
  module Madmin
2
4
  class Engine < ::Rails::Engine
3
5
  isolate_namespace Madmin
@@ -9,6 +11,24 @@ module Madmin
9
11
 
10
12
  config.to_prepare do
11
13
  Madmin.reset_resources!
14
+ Madmin.site_name ||= Rails.application.class.module_parent_name
15
+ end
16
+
17
+ initializer "madmin.assets" do |app|
18
+ if app.config.respond_to?(:assets)
19
+ app.config.assets.paths << root.join("app/assets/stylesheets")
20
+ app.config.assets.paths << root.join("app/javascript")
21
+ app.config.assets.precompile += %w[madmin_manifest]
22
+ end
23
+ end
24
+
25
+ initializer "madmin.importmap", before: "importmap" do |app|
26
+ Madmin.importmap.draw root.join("config/importmap.rb")
27
+ Madmin.importmap.cache_sweeper watches: root.join("app/javascript")
28
+
29
+ ActiveSupport.on_load(:action_controller_base) do
30
+ before_action { Madmin.importmap.cache_sweeper.execute_if_updated }
31
+ end
12
32
  end
13
33
  end
14
34
  end
data/lib/madmin/field.rb CHANGED
@@ -6,8 +6,8 @@ module Madmin
6
6
  to_s.split("::").last.underscore
7
7
  end
8
8
 
9
- def initialize(attribute_name:, model:, resource:, **options)
10
- @attribute_name = attribute_name
9
+ def initialize(attribute_name:, model:, resource:, options:)
10
+ @attribute_name = attribute_name.to_sym
11
11
  @model = model
12
12
  @resource = resource
13
13
  @options = options
@@ -18,7 +18,7 @@ module Madmin
18
18
  end
19
19
 
20
20
  def to_partial_path(name)
21
- unless %w[index show form].include? name
21
+ unless %w[index show form].include? name.to_s
22
22
  raise ArgumentError, "`partial` must be 'index', 'show', or 'form'"
23
23
  end
24
24
 
@@ -30,8 +30,20 @@ module Madmin
30
30
  end
31
31
 
32
32
  # Used for checking visibility of attribute on an view
33
- def visible?(action, default: true)
34
- options.fetch(action.to_sym, default)
33
+ def visible?(action)
34
+ action = action.to_sym
35
+ options.fetch(action) do
36
+ case action
37
+ when :index
38
+ default_index_attributes.include?(attribute_name)
39
+ else
40
+ true
41
+ end
42
+ end
43
+ end
44
+
45
+ def default_index_attributes
46
+ [model.primary_key.to_sym, :avatar, :title, :name, :user, :created_at]
35
47
  end
36
48
 
37
49
  def required?
@@ -2,12 +2,13 @@ module Madmin
2
2
  module Fields
3
3
  class BelongsTo < Field
4
4
  def options_for_select(record)
5
- if (record = record.send(attribute_name))
6
- resource = Madmin.resource_for(record)
7
- [[resource.display_name(record), record.id]]
5
+ records = if (record = record.send(attribute_name))
6
+ [record]
8
7
  else
9
- []
8
+ associated_resource.model.first(25)
10
9
  end
10
+
11
+ records.map { [Madmin.resource_for(_1).display_name(_1), _1.id] }
11
12
  end
12
13
 
13
14
  def to_param
@@ -15,7 +16,11 @@ module Madmin
15
16
  end
16
17
 
17
18
  def index_path
18
- Madmin.resource_by_name(model.reflect_on_association(attribute_name).klass).index_path(format: :json)
19
+ associated_resource.index_path(format: :json)
20
+ end
21
+
22
+ def associated_resource
23
+ Madmin.resource_by_name(model.reflect_on_association(attribute_name).klass)
19
24
  end
20
25
  end
21
26
  end
@@ -0,0 +1,15 @@
1
+ module Madmin
2
+ module Fields
3
+ class Currency < Field
4
+ def value(record)
5
+ value = record.public_send(attribute_name)
6
+ value /= 100.0 if value && options.minor_units
7
+ value
8
+ end
9
+
10
+ def searchable?
11
+ options.fetch(:searchable, model.column_names.include?(attribute_name.to_s))
12
+ end
13
+ end
14
+ end
15
+ end
@@ -5,7 +5,7 @@ module Madmin
5
5
  if (collection = options[:collection])
6
6
  collection.call
7
7
  else
8
- []
8
+ [value(record)].compact
9
9
  end
10
10
  end
11
11
 
@@ -0,0 +1,9 @@
1
+ module Madmin
2
+ module Fields
3
+ class Select < Field
4
+ def options_for_select(record)
5
+ options.collection
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,70 @@
1
+ module Madmin
2
+ class Menu
3
+ def initialize
4
+ @children = {}
5
+ end
6
+
7
+ def reset
8
+ @children = {}
9
+ end
10
+
11
+ def before_render(&block)
12
+ if block_given?
13
+ @before_render = block
14
+ else
15
+ @before_render
16
+ end
17
+ end
18
+
19
+ def render(&block)
20
+ instance_eval(&@before_render) if @before_render
21
+
22
+ # Ensure all the resources have been added to the menu
23
+ Madmin.resources.each do |resource|
24
+ next if resource.menu_options == false
25
+ add resource.menu_options
26
+ end
27
+
28
+ items.each(&block)
29
+ end
30
+
31
+ module Node
32
+ def add(options)
33
+ options = options.dup
34
+
35
+ if (parent = options.delete(:parent))
36
+ @children[parent] ||= Item.new(label: parent)
37
+ @children[parent].add options
38
+ else
39
+ item = Item.new(**options)
40
+ @children[item.label] = item
41
+ end
42
+ end
43
+
44
+ def items
45
+ @children.values.sort do |a, b|
46
+ result = a.position <=> b.position
47
+ result = a.label <=> b.label if result == 0 # sort alphabetically for the same position
48
+ result
49
+ end
50
+ end
51
+ end
52
+
53
+ include Node
54
+
55
+ class Item
56
+ include Node
57
+
58
+ attr_reader :label, :url, :position, :parent, :children
59
+
60
+ def initialize(label:, url: nil, position: 99, parent: nil, **options)
61
+ @label = label
62
+ @url = url
63
+ @position = position
64
+ @parent = parent
65
+ @if = options.delete(:if)
66
+ @children = {}
67
+ end
68
+ end
69
+ end
70
+ end