madmin 1.2.11 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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 +1 -4
  42. data/app/views/madmin/fields/enum/_form.html.erb +1 -4
  43. data/app/views/madmin/fields/file/_form.html.erb +1 -4
  44. data/app/views/madmin/fields/float/_form.html.erb +1 -4
  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 +1 -4
  48. data/app/views/madmin/fields/integer/_index.html.erb +1 -5
  49. data/app/views/madmin/fields/json/_form.html.erb +1 -4
  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 +1 -4
  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 +1 -4
  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 +1 -4
  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
- <%= form.number_field field.attribute_name, step: :any, class: "flex-grow form-input" %>
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
- <%= form.file_field field.attribute_name, class: "flex-grow form-input" %>
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
- <%= form.number_field field.attribute_name, step: :any, class: "flex-grow form-input" %>
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
- <%= form.number_field field.attribute_name, class: "flex-grow form-input" %>
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
- <%= form.text_area field.attribute_name, class: "flex-grow form-input" %>
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
- <%= form.password_field field.attribute_name, class: "flex-grow form-input" %>
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: "flex-grow 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: "flex-grow 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
- <%= form.text_area field.attribute_name, class: "flex-grow form-input" %>
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
- <%= form.text_field field.attribute_name, class: "flex-grow form-input" %>
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