madmin 2.6.0 → 3.1.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 (88) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +120 -4
  3. data/app/assets/stylesheets/madmin/actiontext.css +31 -0
  4. data/app/assets/stylesheets/madmin/base.css +22 -11
  5. data/app/assets/stylesheets/madmin/buttons.css +9 -9
  6. data/app/assets/stylesheets/madmin/forms.css +263 -7
  7. data/app/assets/stylesheets/madmin/pagination.css +10 -23
  8. data/app/assets/stylesheets/madmin/reset.css +1 -1
  9. data/app/assets/stylesheets/madmin/sidebar.css +6 -6
  10. data/app/assets/stylesheets/madmin/tables.css +1 -1
  11. data/app/controllers/madmin/base_controller.rb +1 -5
  12. data/app/controllers/madmin/resource_controller.rb +38 -17
  13. data/app/helpers/madmin/application_helper.rb +15 -2
  14. data/app/helpers/madmin/filters_helper.rb +26 -0
  15. data/app/helpers/madmin/sort_helper.rb +1 -1
  16. data/app/javascript/madmin/application.js +4 -0
  17. data/app/javascript/madmin/controllers/application.js +0 -3
  18. data/app/javascript/madmin/controllers/filters_controller.js +35 -0
  19. data/app/javascript/madmin/controllers/nested_form_controller.js +5 -0
  20. data/app/javascript/madmin/controllers/select_controller.js +269 -22
  21. data/app/views/layouts/madmin/application.html.erb +1 -0
  22. data/app/views/madmin/application/_active_filters.html.erb +15 -0
  23. data/app/views/madmin/application/_filter.html.erb +13 -0
  24. data/app/views/madmin/application/_filters.html.erb +32 -0
  25. data/app/views/madmin/application/_javascript.html.erb +0 -2
  26. data/app/views/madmin/application/_pagination.html.erb +32 -0
  27. data/app/views/madmin/application/edit.html.erb +1 -1
  28. data/app/views/madmin/application/index.html.erb +13 -12
  29. data/app/views/madmin/application/new.html.erb +1 -1
  30. data/app/views/madmin/application/show.html.erb +1 -1
  31. data/app/views/madmin/fields/array/_form.html.erb +1 -0
  32. data/app/views/madmin/fields/array/_index.html.erb +1 -0
  33. data/app/views/madmin/fields/array/_show.html.erb +1 -0
  34. data/app/views/madmin/fields/attachment/_form.html.erb +1 -5
  35. data/app/views/madmin/fields/attachment/_show.html.erb +3 -1
  36. data/app/views/madmin/fields/attachments/_form.html.erb +1 -1
  37. data/app/views/madmin/fields/attachments/_index.html.erb +1 -1
  38. data/app/views/madmin/fields/belongs_to/_form.html.erb +1 -1
  39. data/app/views/madmin/fields/belongs_to/_index.html.erb +5 -2
  40. data/app/views/madmin/fields/belongs_to/_show.html.erb +1 -1
  41. data/app/views/madmin/fields/enum/_form.html.erb +1 -1
  42. data/app/views/madmin/fields/file/_form.html.erb +1 -1
  43. data/app/views/madmin/fields/has_many/_form.html.erb +1 -1
  44. data/app/views/madmin/fields/has_many/_index.html.erb +1 -1
  45. data/app/views/madmin/fields/has_many/_show.html.erb +3 -11
  46. data/app/views/madmin/fields/has_one/_index.html.erb +5 -1
  47. data/app/views/madmin/fields/has_one/_show.html.erb +1 -1
  48. data/app/views/madmin/fields/json/_form.html.erb +1 -1
  49. data/app/views/madmin/fields/json/_show.html.erb +1 -1
  50. data/app/views/madmin/fields/nested_has_many/_index.html.erb +1 -1
  51. data/app/views/madmin/fields/nested_has_many/_show.html.erb +3 -11
  52. data/app/views/madmin/fields/nested_has_one/_form.html.erb +18 -0
  53. data/app/views/madmin/fields/polymorphic/_form.html.erb +1 -2
  54. data/app/views/madmin/fields/polymorphic/_index.html.erb +5 -1
  55. data/app/views/madmin/fields/polymorphic/_show.html.erb +1 -1
  56. data/app/views/madmin/fields/select/_form.html.erb +1 -1
  57. data/config/importmap.rb +0 -3
  58. data/config/locales/el.yml +44 -0
  59. data/config/locales/en.yml +44 -0
  60. data/config/locales/fr.yml +44 -0
  61. data/config/locales/ko.yml +44 -0
  62. data/lib/madmin/field.rb +27 -1
  63. data/lib/madmin/fields/array.rb +16 -0
  64. data/lib/madmin/fields/attachment.rb +7 -0
  65. data/lib/madmin/fields/belongs_to.rb +2 -1
  66. data/lib/madmin/fields/boolean.rb +1 -0
  67. data/lib/madmin/fields/currency.rb +7 -0
  68. data/lib/madmin/fields/date.rb +1 -0
  69. data/lib/madmin/fields/date_time.rb +1 -0
  70. data/lib/madmin/fields/decimal.rb +1 -0
  71. data/lib/madmin/fields/float.rb +1 -0
  72. data/lib/madmin/fields/has_many.rb +6 -23
  73. data/lib/madmin/fields/integer.rb +1 -0
  74. data/lib/madmin/fields/json.rb +21 -0
  75. data/lib/madmin/fields/nested_has_many.rb +2 -2
  76. data/lib/madmin/fields/nested_has_one.rb +48 -0
  77. data/lib/madmin/fields/polymorphic.rb +5 -0
  78. data/lib/madmin/fields/string.rb +2 -0
  79. data/lib/madmin/fields/text.rb +2 -0
  80. data/lib/madmin/filter.rb +96 -0
  81. data/lib/madmin/menu.rb +2 -1
  82. data/lib/madmin/page.rb +61 -0
  83. data/lib/madmin/pagination.rb +50 -0
  84. data/lib/madmin/resource.rb +85 -7
  85. data/lib/madmin/search.rb +5 -7
  86. data/lib/madmin/version.rb +1 -1
  87. data/lib/madmin.rb +45 -2
  88. metadata +17 -16
data/lib/madmin/search.rb CHANGED
@@ -2,20 +2,18 @@
2
2
 
3
3
  module Madmin
4
4
  class Search
5
- attr_reader :query
5
+ attr_reader :query, :filters
6
6
 
7
- def initialize(scoped_resource, resource, term)
7
+ def initialize(scoped_resource, resource, term, filters = [])
8
8
  @resource = resource
9
9
  @scoped_resource = scoped_resource
10
10
  @query = term
11
+ @filters = filters
11
12
  end
12
13
 
13
14
  def run
14
- if query.blank?
15
- @scoped_resource.all
16
- else
17
- search_results(@scoped_resource)
18
- end
15
+ results = query.blank? ? @scoped_resource.all : search_results(@scoped_resource)
16
+ filters.reduce(results) { |scope, filter| filter.apply(scope) }
19
17
  end
20
18
 
21
19
  private
@@ -1,3 +1,3 @@
1
1
  module Madmin
2
- VERSION = "2.6.0"
2
+ VERSION = "3.1.0"
3
3
  end
data/lib/madmin.rb CHANGED
@@ -2,18 +2,21 @@ require "madmin/engine"
2
2
  require "importmap-rails"
3
3
  require "stimulus-rails"
4
4
  require "turbo-rails"
5
- require "pagy"
6
5
 
7
6
  module Madmin
8
7
  autoload :Field, "madmin/field"
8
+ autoload :Filter, "madmin/filter"
9
9
  autoload :GeneratorHelpers, "madmin/generator_helpers"
10
10
  autoload :MemberAction, "madmin/member_action"
11
11
  autoload :Menu, "madmin/menu"
12
+ autoload :Page, "madmin/page"
13
+ autoload :Pagination, "madmin/pagination"
12
14
  autoload :Resource, "madmin/resource"
13
15
  autoload :ResourceBuilder, "madmin/resource_builder"
14
16
  autoload :Search, "madmin/search"
15
17
 
16
18
  module Fields
19
+ autoload :Array, "madmin/fields/array"
17
20
  autoload :Attachment, "madmin/fields/attachment"
18
21
  autoload :Attachments, "madmin/fields/attachments"
19
22
  autoload :BelongsTo, "madmin/fields/belongs_to"
@@ -30,6 +33,7 @@ module Madmin
30
33
  autoload :Integer, "madmin/fields/integer"
31
34
  autoload :Json, "madmin/fields/json"
32
35
  autoload :NestedHasMany, "madmin/fields/nested_has_many"
36
+ autoload :NestedHasOne, "madmin/fields/nested_has_one"
33
37
  autoload :Password, "madmin/fields/password"
34
38
  autoload :Polymorphic, "madmin/fields/polymorphic"
35
39
  autoload :RichText, "madmin/fields/rich_text"
@@ -41,6 +45,7 @@ module Madmin
41
45
 
42
46
  mattr_accessor :importmap, default: Importmap::Map.new
43
47
  mattr_accessor :menu, default: Menu.new
48
+ mattr_accessor :per_page, default: 20
44
49
  mattr_accessor :site_name
45
50
  mattr_accessor :stylesheets, default: []
46
51
  mattr_accessor :resource_locations, default: []
@@ -48,6 +53,9 @@ module Madmin
48
53
  class MissingResource < StandardError
49
54
  end
50
55
 
56
+ class MissingRoute < StandardError
57
+ end
58
+
51
59
  class << self
52
60
  # Returns a Madmin::Resource class for the given object
53
61
  def resource_for(object)
@@ -58,6 +66,12 @@ module Madmin
58
66
  elsif (resource_name = sti_resource_name_for(object)) && Object.const_defined?(resource_name)
59
67
  resource_name.constantize
60
68
 
69
+ # A resource named differently from its model (`ArticleResource` for
70
+ # `Blog::Post`) still says which model it is for with `model`. Honor that
71
+ # declaration before giving up, so association cells can link to it.
72
+ elsif (resource = resource_declaring(object.class))
73
+ resource
74
+
61
75
  else
62
76
  raise MissingResource, <<~MESSAGE
63
77
  `#{object.class.name}Resource` is missing.
@@ -69,6 +83,28 @@ module Madmin
69
83
  end
70
84
  end
71
85
 
86
+ # The one resource whose `model` is +klass+ or, failing that, its nearest
87
+ # superclass, or nil. Two resources declaring the same model with neither
88
+ # matching its name is ambiguous, and guessing would silently link to the
89
+ # wrong admin page, so that raises.
90
+ def resource_declaring(klass)
91
+ declared = klass.ancestors.grep(Class).find { |ancestor| resources_by_model.key?(ancestor) }
92
+ return unless declared
93
+
94
+ candidates = resources_by_model[declared]
95
+ return candidates.first if candidates.one?
96
+
97
+ raise MissingResource, <<~MESSAGE
98
+ `#{declared.name}Resource` is missing, and #{candidates.map(&:name).join(", ")} all declare `model #{declared.name}`.
99
+
100
+ Madmin can't tell which one to use. Name one of them `#{declared.name}Resource`.
101
+ MESSAGE
102
+ end
103
+
104
+ def resources_by_model
105
+ @resources_by_model ||= resources.group_by(&:model)
106
+ end
107
+
72
108
  def resource_name_for(object)
73
109
  if object.is_a? ::ActiveStorage::Attached
74
110
  "ActiveStorage::AttachmentResource"
@@ -85,13 +121,19 @@ module Madmin
85
121
  end
86
122
  end
87
123
 
124
+ # Returns the Madmin::Resource class for a model or model name, falling
125
+ # back to the resource that declares the model when none is named after it
88
126
  def resource_by_name(name)
89
127
  "#{name}Resource".constantize
90
128
  rescue NameError
129
+ model = name.is_a?(Class) ? name : name.to_s.safe_constantize
130
+ resource = resource_declaring(model) if model
131
+ return resource if resource
132
+
91
133
  raise MissingResource, <<~MESSAGE
92
134
  #{name}Resource is missing. Create it by running:
93
135
 
94
- bin/rails generate madmin:resource #{resource_name.split("Resource").first}
136
+ bin/rails generate madmin:resource #{name}
95
137
  MESSAGE
96
138
  end
97
139
 
@@ -101,6 +143,7 @@ module Madmin
101
143
 
102
144
  def reset_resources!
103
145
  @resources = nil
146
+ @resources_by_model = nil
104
147
  menu.reset
105
148
  end
106
149
 
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.6.0
4
+ version: 3.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Oliver
@@ -24,20 +24,6 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: 7.0.0
27
- - !ruby/object:Gem::Dependency
28
- name: pagy
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '3.5'
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '3.5'
41
27
  - !ruby/object:Gem::Dependency
42
28
  name: importmap-rails
43
29
  requirement: !ruby/object:Gem::Requirement
@@ -107,25 +93,34 @@ files:
107
93
  - app/controllers/madmin/dashboard_controller.rb
108
94
  - app/controllers/madmin/resource_controller.rb
109
95
  - app/helpers/madmin/application_helper.rb
96
+ - app/helpers/madmin/filters_helper.rb
110
97
  - app/helpers/madmin/nav_helper.rb
111
98
  - app/helpers/madmin/sort_helper.rb
112
99
  - app/javascript/madmin/application.js
113
100
  - app/javascript/madmin/controllers/application.js
101
+ - app/javascript/madmin/controllers/filters_controller.js
114
102
  - app/javascript/madmin/controllers/index.js
115
103
  - app/javascript/madmin/controllers/mobile_nav_controller.js
116
104
  - app/javascript/madmin/controllers/nested_form_controller.js
117
105
  - app/javascript/madmin/controllers/select_controller.js
118
106
  - app/views/layouts/madmin/application.html.erb
107
+ - app/views/madmin/application/_active_filters.html.erb
108
+ - app/views/madmin/application/_filter.html.erb
109
+ - app/views/madmin/application/_filters.html.erb
119
110
  - app/views/madmin/application/_flash.html.erb
120
111
  - app/views/madmin/application/_form.html.erb
121
112
  - app/views/madmin/application/_javascript.html.erb
122
113
  - app/views/madmin/application/_missing_resource.html.erb
123
114
  - app/views/madmin/application/_navigation.html.erb
115
+ - app/views/madmin/application/_pagination.html.erb
124
116
  - app/views/madmin/application/edit.html.erb
125
117
  - app/views/madmin/application/index.html.erb
126
118
  - app/views/madmin/application/new.html.erb
127
119
  - app/views/madmin/application/show.html.erb
128
120
  - app/views/madmin/dashboard/show.html.erb
121
+ - app/views/madmin/fields/array/_form.html.erb
122
+ - app/views/madmin/fields/array/_index.html.erb
123
+ - app/views/madmin/fields/array/_show.html.erb
129
124
  - app/views/madmin/fields/attachment/_form.html.erb
130
125
  - app/views/madmin/fields/attachment/_index.html.erb
131
126
  - app/views/madmin/fields/attachment/_show.html.erb
@@ -175,6 +170,7 @@ files:
175
170
  - app/views/madmin/fields/nested_has_many/_form.html.erb
176
171
  - app/views/madmin/fields/nested_has_many/_index.html.erb
177
172
  - app/views/madmin/fields/nested_has_many/_show.html.erb
173
+ - app/views/madmin/fields/nested_has_one/_form.html.erb
178
174
  - app/views/madmin/fields/password/_form.html.erb
179
175
  - app/views/madmin/fields/password/_index.html.erb
180
176
  - app/views/madmin/fields/password/_show.html.erb
@@ -225,6 +221,7 @@ files:
225
221
  - lib/madmin.rb
226
222
  - lib/madmin/engine.rb
227
223
  - lib/madmin/field.rb
224
+ - lib/madmin/fields/array.rb
228
225
  - lib/madmin/fields/attachment.rb
229
226
  - lib/madmin/fields/attachments.rb
230
227
  - lib/madmin/fields/belongs_to.rb
@@ -241,6 +238,7 @@ files:
241
238
  - lib/madmin/fields/integer.rb
242
239
  - lib/madmin/fields/json.rb
243
240
  - lib/madmin/fields/nested_has_many.rb
241
+ - lib/madmin/fields/nested_has_one.rb
244
242
  - lib/madmin/fields/password.rb
245
243
  - lib/madmin/fields/polymorphic.rb
246
244
  - lib/madmin/fields/rich_text.rb
@@ -248,10 +246,13 @@ files:
248
246
  - lib/madmin/fields/string.rb
249
247
  - lib/madmin/fields/text.rb
250
248
  - lib/madmin/fields/time.rb
249
+ - lib/madmin/filter.rb
251
250
  - lib/madmin/generator_helpers.rb
252
251
  - lib/madmin/member_action.rb
253
252
  - lib/madmin/menu.rb
254
253
  - lib/madmin/namespace.rb
254
+ - lib/madmin/page.rb
255
+ - lib/madmin/pagination.rb
255
256
  - lib/madmin/resource.rb
256
257
  - lib/madmin/resource_builder.rb
257
258
  - lib/madmin/search.rb
@@ -276,7 +277,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
276
277
  - !ruby/object:Gem::Version
277
278
  version: '0'
278
279
  requirements: []
279
- rubygems_version: 4.0.16
280
+ rubygems_version: 4.0.20
280
281
  specification_version: 4
281
282
  summary: A modern admin for Ruby on Rails apps
282
283
  test_files: []