madmin 2.3.3 → 2.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2862cda2a9f7f8cc5313b205404c21bc4bd2e3a9aa780dfe6ba708ef0db51c34
4
- data.tar.gz: a6dd1ac4898938c31ab694c61ca9c7ac379510b3ab00cc9830d8df43d651aff5
3
+ metadata.gz: 0bfdf6827cb1ef98872c55b9c786c08d9e9f8def47a2a9fa8bc5a3552c3bbee8
4
+ data.tar.gz: b4c73bdb4cfebfc209e7edce32d926c0e32ef9a291c8404c755665e2bbdc62f3
5
5
  SHA512:
6
- metadata.gz: 8958ec3db4a60cd9df3484a9eb864f6b273ef646ae5a631f13cc16f0d7870147998193552de77c4cef6aad57bf3d97db8bd3f18fa213304f35b430f5d60ac8c7
7
- data.tar.gz: d0eb3ec050476cdd04beec7d6ec2cb3a5a1a10d47dbab182b19236b2d8d3a0b90bebdbda04a0e8f5f875a2b695379095a3681470178669a9bc23ccdcb1e55a22
6
+ metadata.gz: 4d973242c54c9f3da6f0fbf8110b8c14b221d1d63841c1056d2e0310a4464869f638c7e20dad217a9d5f014853258fb482b40e84642462afeeda7a39af0477f0
7
+ data.tar.gz: 349dc39b717ce028990db3935de771396b0d6a62f877997b37231447d9fed6163bf57651245a9b5bb085c3edb37f8cf3c28e748ea546c3c45ab59da6e68241d2
data/README.md CHANGED
@@ -158,6 +158,40 @@ class PostResource < Madmin::Resource
158
158
  end
159
159
  ```
160
160
 
161
+ ## Actions
162
+
163
+ ### Member Actions
164
+
165
+ `member_action` lets you add custom buttons to a resource's **show** page. Each block receives the current `record` and is rendered in the header's `.actions` div:
166
+
167
+ ```ruby
168
+ class PostResource < Madmin::Resource
169
+ member_action do |record|
170
+ link_to "Publish", publish_admin_post_path(record), class: "btn btn-secondary"
171
+ end
172
+ end
173
+ ```
174
+
175
+ ### Collection Actions
176
+
177
+ `collection_action` is the index-page counterpart to `member_action`. Blocks are rendered in the index header's `.actions` div, immediately before the built-in "New \<Resource\>" link.
178
+
179
+ Unlike `member_action`, blocks receive **no** arguments. Each block is `instance_exec`-ed on the index view context, so `link_to`, route helpers, `policy`, `current_user`, `params`, `resource`, and `@records` are all available. Multiple blocks render in registration order.
180
+
181
+ Each block is responsible for its own authorization gating — if a button should be hidden for some users, guard it inside the block.
182
+
183
+ ```ruby
184
+ class PostResource < Madmin::Resource
185
+ collection_action do
186
+ link_to "Bulk Import", bulk_import_admin_posts_path, class: "btn btn-secondary"
187
+ end
188
+
189
+ collection_action do
190
+ link_to "Export CSV", export_admin_posts_path(format: :csv), class: "btn btn-secondary"
191
+ end
192
+ end
193
+ ```
194
+
161
195
  ## Authentication
162
196
 
163
197
  You can use a couple of strategies to authenticate users who are trying to
@@ -8,7 +8,7 @@
8
8
  <% if content_for? :title %>
9
9
  <%= yield(:title) %> -
10
10
  <% end %>
11
- <%= Madmin.site_name %> Admin
11
+ <%= Madmin.site_name %> <%= t("madmin.layout.admin_suffix") %>
12
12
  </title>
13
13
  <%= csrf_meta_tags %>
14
14
  <%= render "javascript" %>
@@ -19,7 +19,7 @@
19
19
  </header>
20
20
  <header id="navbar">
21
21
  <div class="nav-group">
22
- <button id="hamburger-menu" type="button" data-action="click->mobile-nav#toggle" aria-label="Open menu" aria-expanded="false" aria-controls="sidebar">
22
+ <button id="hamburger-menu" type="button" data-action="click->mobile-nav#toggle" aria-label="<%= t("madmin.layout.open_menu") %>" aria-expanded="false" aria-controls="sidebar">
23
23
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-5"> <path fill-rule="evenodd" d="M2 4.75A.75.75 0 0 1 2.75 4h14.5a.75.75 0 0 1 0 1.5H2.75A.75.75 0 0 1 2 4.75ZM2 10a.75.75 0 0 1 .75-.75h14.5a.75.75 0 0 1 0 1.5H2.75A.75.75 0 0 1 2 10Zm0 5.25a.75.75 0 0 1 .75-.75h14.5a.75.75 0 0 1 0 1.5H2.75a.75.75 0 0 1-.75-.75Z" clip-rule="evenodd" /> </svg>
24
24
  </button>
25
25
  <%= link_to Madmin.site_name, try(:root_url) || madmin_root_url, data: {turbo: false} %>
@@ -1,7 +1,7 @@
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
3
  <div class="alert alert-danger">
4
- <div class="">There was <%= pluralize form.object.errors.full_messages.count, "error" %> with your submission:</div>
4
+ <div class=""><%= t("madmin.form.errors", count: form.object.errors.full_messages.count) %>:</div>
5
5
 
6
6
  <ul>
7
7
  <% form.object.errors.full_messages.each do |message| %>
@@ -20,5 +20,5 @@
20
20
  <% end %>
21
21
 
22
22
  <%= form.submit class: "btn btn-primary" %>
23
- <%= link_to "Cancel", (record.persisted? ? resource.show_path(record) : resource.index_path), class: "btn" %>
23
+ <%= link_to t("madmin.actions.cancel"), (record.persisted? ? resource.show_path(record) : resource.index_path), class: "btn" %>
24
24
  <% end %>
@@ -1,6 +1,6 @@
1
- <div class="error"><%= resource_name %> is missing.</div>
1
+ <div class="error"><%= t("madmin.missing_resource.message", resource: resource_name) %></div>
2
2
  <% if Rails.env.development? %>
3
- <div>Create it by running:
3
+ <div><%= t("madmin.missing_resource.create_hint") %>
4
4
  <code>
5
5
  bin/rails generate madmin:resource <%= resource_name.split("Resource").first %>
6
6
  </code>
@@ -1,5 +1,5 @@
1
1
  <nav>
2
- <%= nav_link_to "Dashboard", madmin_root_path %>
2
+ <%= nav_link_to t("madmin.navigation.dashboard"), madmin_root_path %>
3
3
 
4
4
  <% Madmin.menu.render do |item| %>
5
5
  <% if item.url %>
@@ -17,6 +17,6 @@
17
17
  <footer>
18
18
  <%= link_to "https://github.com/excid3/madmin", target: :_blank do %>
19
19
  <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>
20
- Madmin on GitHub
20
+ <%= t("madmin.navigation.github") %>
21
21
  <% end %>
22
22
  </footer>
@@ -1,10 +1,10 @@
1
- <%= content_for :title, "Edit #{resource.display_name(@record)}" %>
1
+ <%= content_for :title, t("madmin.titles.edit", name: resource.display_name(@record)) %>
2
2
 
3
3
  <header class="header">
4
4
  <h1>
5
5
  <%= link_to resource.friendly_name.pluralize, resource.index_path %>
6
6
  /
7
- <strong>Edit <%= link_to resource.display_name(@record), resource.show_path(@record) %></strong>
7
+ <strong><%= t("madmin.actions.edit") %> <%= link_to resource.display_name(@record), resource.show_path(@record) %></strong>
8
8
  </h1>
9
9
  </header>
10
10
 
@@ -6,7 +6,7 @@
6
6
  <div class="actions">
7
7
  <form class="search">
8
8
  <%= hidden_field_tag :page, params[:page], value: 1 %>
9
- <%= search_field_tag :q, params[:q], placeholder: "Search" %>
9
+ <%= search_field_tag :q, params[:q], placeholder: t("madmin.search.placeholder") %>
10
10
  </form>
11
11
 
12
12
  <%= link_to clear_search_params, class: "btn btn-secondary" do %>
@@ -15,15 +15,19 @@
15
15
  </svg>
16
16
  <% end if params[:q].present? %>
17
17
 
18
+ <% resource.collection_actions.each do |action| %>
19
+ <%= instance_exec(&action) %>
20
+ <% end %>
21
+
18
22
  <%= link_to resource.new_path, class: "btn btn-secondary" do %>
19
- New <%= tag.span resource.friendly_name, class: "hidden md:inline-block" %>
23
+ <%= t("madmin.actions.new") %> <%= tag.span resource.friendly_name, class: "hidden md:inline-block" %>
20
24
  <% end %>
21
25
  </div>
22
26
  </header>
23
27
 
24
28
  <nav class="scopes">
25
29
  <% if resource.scopes.any? %>
26
- <%= link_to "All", resource.index_path, class: class_names("btn btn-secondary", {"active" => params[:scope].blank?}) %>
30
+ <%= link_to t("madmin.actions.all"), resource.index_path, class: class_names("btn btn-secondary", {"active" => params[:scope].blank?}) %>
27
31
  <% end %>
28
32
 
29
33
  <% resource.scopes.each do |scope| %>
@@ -55,8 +59,8 @@
55
59
  <% end %>
56
60
 
57
61
  <td>
58
- <%= link_to "View", resource.show_path(record) %>
59
- <%= link_to "Edit", resource.edit_path(record) %>
62
+ <%= link_to t("madmin.actions.view"), resource.show_path(record) %>
63
+ <%= link_to t("madmin.actions.edit"), resource.edit_path(record) %>
60
64
  </td>
61
65
  </tr>
62
66
  <% end %>
@@ -1,10 +1,10 @@
1
- <%= content_for :title, "New #{resource.friendly_name}" %>
1
+ <%= content_for :title, t("madmin.titles.new", name: resource.friendly_name) %>
2
2
 
3
3
  <header class="header">
4
4
  <h1>
5
5
  <%= link_to resource.friendly_name.pluralize, resource.index_path %>
6
6
  /
7
- <strong>New <%= resource.friendly_name %></strong>
7
+ <strong><%= t("madmin.titles.new", name: resource.friendly_name) %></strong>
8
8
  </h1>
9
9
  </header>
10
10
 
@@ -11,8 +11,8 @@
11
11
  <% resource.member_actions.each do |action| %>
12
12
  <%= instance_exec(@record, &action) %>
13
13
  <% end %>
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
+ <%= link_to t("madmin.actions.edit"), resource.edit_path(@record), class: "btn btn-secondary" %>
15
+ <%= button_to t("madmin.actions.delete"), resource.show_path(@record), method: :delete, data: { turbo_confirm: t("madmin.confirmations.delete") }, class: "btn btn-danger" %>
16
16
  </div>
17
17
  </header>
18
18
 
@@ -1,2 +1,2 @@
1
- <h1>Madmin Dashboard</h1>
2
- <p>Create <code>app/views/madmin/dashboard/show.html.erb</code> to customize your dashboard.</p>
1
+ <h1><%= t("madmin.dashboard.title") %></h1>
2
+ <p><%= t("madmin.dashboard.hint_html") %></p>
@@ -5,7 +5,7 @@
5
5
  <%= render partial: field.to_partial_path("show"), locals: {field: field, record: record} %>
6
6
 
7
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."} %>
8
+ <%= link_to t("madmin.actions.remove"), Madmin.resource_for(value).show_path(value), data: { turbo_method: :delete, turbo_confirm: t("madmin.confirmations.remove_with_changes")} %>
9
9
  <% end %>
10
10
  </div>
11
11
  <% end %>
@@ -5,5 +5,5 @@
5
5
  <%= link_to attachment.filename, main_app.url_for(attachment), target: :_blank, class: "text-blue-500 underline" %>
6
6
  <% end %>
7
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."} %>
8
+ <%= link_to t("madmin.actions.remove"), Madmin.resource_for(attachment).show_path(attachment), data: { turbo_method: :delete, turbo_confirm: t("madmin.confirmations.remove_with_changes")} %>
9
9
  <% end %>
@@ -1 +1 @@
1
- <%= pluralize field.value(record).count, "attachment" %>
1
+ <%= t("madmin.fields.attachment", count: field.value(record).count) %>
@@ -11,7 +11,7 @@
11
11
  </div>
12
12
  <% end %>
13
13
 
14
- <small><%= link_to "Remove", "#", data: { action: "click->nested-form#remove_association" } %></small>
14
+ <small><%= link_to t("madmin.actions.remove"), "#", data: { action: "click->nested-form#remove_association" } %></small>
15
15
 
16
16
  <%= f.hidden_field :_destroy %>
17
17
  <% end %>
@@ -23,6 +23,6 @@
23
23
  <% end %>
24
24
 
25
25
  <%= content_tag :div, class: '', data: { target:"nested-form.links" } do %>
26
- <%= link_to "+ Add new", "#", data: { action: "click->nested-form#add_association" } %>
26
+ <%= link_to t("madmin.nested_form.add_new"), "#", data: { action: "click->nested-form#add_association" } %>
27
27
  <% end %>
28
28
  </div>
@@ -0,0 +1,40 @@
1
+ en:
2
+ madmin:
3
+ layout:
4
+ open_menu: Open menu
5
+ admin_suffix: Admin
6
+ navigation:
7
+ dashboard: Dashboard
8
+ github: Madmin on GitHub
9
+ dashboard:
10
+ title: Madmin Dashboard
11
+ hint_html: Create <code>app/views/madmin/dashboard/show.html.erb</code> to customize your dashboard.
12
+ actions:
13
+ all: All
14
+ view: View
15
+ edit: Edit
16
+ delete: Delete
17
+ new: New
18
+ remove: Remove
19
+ cancel: Cancel
20
+ search:
21
+ placeholder: Search
22
+ titles:
23
+ new: New %{name}
24
+ edit: Edit %{name}
25
+ form:
26
+ errors:
27
+ one: There was 1 error with your submission
28
+ other: There were %{count} errors with your submission
29
+ confirmations:
30
+ delete: Are you sure?
31
+ remove_with_changes: Are you sure? You will lose any other changes.
32
+ nested_form:
33
+ add_new: "+ Add new"
34
+ fields:
35
+ attachment:
36
+ one: "%{count} attachment"
37
+ other: "%{count} attachments"
38
+ missing_resource:
39
+ message: "%{resource} is missing."
40
+ create_hint: "Create it by running:"
@@ -0,0 +1,40 @@
1
+ fr:
2
+ madmin:
3
+ layout:
4
+ open_menu: Ouvrir le menu
5
+ admin_suffix: Administration
6
+ navigation:
7
+ dashboard: Tableau de bord
8
+ github: Madmin sur GitHub
9
+ dashboard:
10
+ title: Tableau de bord Madmin
11
+ hint_html: Créez <code>app/views/madmin/dashboard/show.html.erb</code> pour personnaliser votre tableau de bord.
12
+ actions:
13
+ all: Tous
14
+ view: Voir
15
+ edit: Modifier
16
+ delete: Supprimer
17
+ new: Nouveau
18
+ remove: Retirer
19
+ cancel: Annuler
20
+ search:
21
+ placeholder: Rechercher
22
+ titles:
23
+ new: Nouveau %{name}
24
+ edit: Modifier %{name}
25
+ form:
26
+ errors:
27
+ one: Il y a eu 1 erreur dans votre soumission
28
+ other: Il y a eu %{count} erreurs dans votre soumission
29
+ confirmations:
30
+ delete: Êtes-vous sûr ?
31
+ remove_with_changes: Êtes-vous sûr ? Vous perdrez toutes les autres modifications.
32
+ nested_form:
33
+ add_new: "+ Ajouter"
34
+ fields:
35
+ attachment:
36
+ one: "%{count} pièce jointe"
37
+ other: "%{count} pièces jointes"
38
+ missing_resource:
39
+ message: "%{resource} est introuvable."
40
+ create_hint: "Pour le créer, exécutez :"
@@ -17,6 +17,11 @@ class <%= class_name %>Resource < Madmin::Resource
17
17
  # link_to "Do Something", some_path
18
18
  # end
19
19
 
20
+ # Add actions to the resource's index page
21
+ # collection_action do
22
+ # link_to "Bulk Import", bulk_import_path, class: "btn btn-secondary"
23
+ # end
24
+
20
25
  # Customize the display name of records in the admin area.
21
26
  # def self.display_name(record) = record.name
22
27
 
data/lib/madmin/engine.rb CHANGED
@@ -1,5 +1,3 @@
1
- require "importmap-rails"
2
-
3
1
  module Madmin
4
2
  class Engine < ::Rails::Engine
5
3
  isolate_namespace Madmin
@@ -4,6 +4,7 @@ module Madmin
4
4
 
5
5
  class_attribute :attributes, default: ActiveSupport::OrderedHash.new
6
6
  class_attribute :member_actions, default: []
7
+ class_attribute :collection_actions, default: []
7
8
  class_attribute :scopes, default: []
8
9
  class_attribute :menu_options, instance_reader: false
9
10
 
@@ -11,6 +12,7 @@ module Madmin
11
12
  def inherited(base)
12
13
  base.attributes = attributes.dup
13
14
  base.member_actions = scopes.dup
15
+ base.collection_actions = collection_actions.dup
14
16
  base.scopes = scopes.dup
15
17
  super
16
18
  end
@@ -136,6 +138,10 @@ module Madmin
136
138
  member_actions << block
137
139
  end
138
140
 
141
+ def collection_action(&block)
142
+ collection_actions << block
143
+ end
144
+
139
145
  def field_for_type(type)
140
146
  {
141
147
  binary: Fields::String,
@@ -1,3 +1,3 @@
1
1
  module Madmin
2
- VERSION = "2.3.3"
2
+ VERSION = "2.4.0"
3
3
  end
data/lib/madmin.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  require "madmin/engine"
2
-
3
2
  require "importmap-rails"
4
3
  require "stimulus-rails"
5
4
  require "turbo-rails"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: madmin
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.3
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Oliver
@@ -38,20 +38,6 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '3.5'
41
- - !ruby/object:Gem::Dependency
42
- name: propshaft
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: '0'
48
- type: :runtime
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- version: '0'
55
41
  - !ruby/object:Gem::Dependency
56
42
  name: importmap-rails
57
43
  requirement: !ruby/object:Gem::Requirement
@@ -212,6 +198,8 @@ files:
212
198
  - app/views/madmin/fields/time/_show.html.erb
213
199
  - app/views/madmin/shared/_label.html.erb
214
200
  - config/importmap.rb
201
+ - config/locales/en.yml
202
+ - config/locales/fr.yml
215
203
  - lib/generators/madmin/field/field_generator.rb
216
204
  - lib/generators/madmin/field/templates/_form.html.erb
217
205
  - lib/generators/madmin/field/templates/_index.html.erb
@@ -285,7 +273,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
285
273
  - !ruby/object:Gem::Version
286
274
  version: '0'
287
275
  requirements: []
288
- rubygems_version: 4.0.12
276
+ rubygems_version: 4.0.18
289
277
  specification_version: 4
290
278
  summary: A modern admin for Ruby on Rails apps
291
279
  test_files: []