madmin 2.5.1 → 3.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 (85) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +121 -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 +5 -1
  11. data/app/controllers/madmin/base_controller.rb +1 -5
  12. data/app/controllers/madmin/resource_controller.rb +49 -16
  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 +2 -2
  16. data/app/javascript/madmin/controllers/filters_controller.js +35 -0
  17. data/app/javascript/madmin/controllers/nested_form_controller.js +5 -0
  18. data/app/javascript/madmin/controllers/select_controller.js +269 -22
  19. data/app/views/layouts/madmin/application.html.erb +1 -0
  20. data/app/views/madmin/application/_active_filters.html.erb +15 -0
  21. data/app/views/madmin/application/_filter.html.erb +13 -0
  22. data/app/views/madmin/application/_filters.html.erb +32 -0
  23. data/app/views/madmin/application/_javascript.html.erb +0 -2
  24. data/app/views/madmin/application/_pagination.html.erb +32 -0
  25. data/app/views/madmin/application/edit.html.erb +1 -1
  26. data/app/views/madmin/application/index.html.erb +18 -15
  27. data/app/views/madmin/application/new.html.erb +1 -1
  28. data/app/views/madmin/application/show.html.erb +5 -3
  29. data/app/views/madmin/fields/array/_form.html.erb +1 -0
  30. data/app/views/madmin/fields/array/_index.html.erb +1 -0
  31. data/app/views/madmin/fields/array/_show.html.erb +1 -0
  32. data/app/views/madmin/fields/attachment/_form.html.erb +0 -4
  33. data/app/views/madmin/fields/attachment/_show.html.erb +3 -1
  34. data/app/views/madmin/fields/attachments/_index.html.erb +1 -1
  35. data/app/views/madmin/fields/belongs_to/_form.html.erb +1 -1
  36. data/app/views/madmin/fields/belongs_to/_index.html.erb +5 -2
  37. data/app/views/madmin/fields/belongs_to/_show.html.erb +1 -1
  38. data/app/views/madmin/fields/enum/_form.html.erb +1 -1
  39. data/app/views/madmin/fields/has_many/_form.html.erb +1 -1
  40. data/app/views/madmin/fields/has_many/_index.html.erb +1 -1
  41. data/app/views/madmin/fields/has_many/_show.html.erb +3 -11
  42. data/app/views/madmin/fields/has_one/_index.html.erb +5 -1
  43. data/app/views/madmin/fields/has_one/_show.html.erb +1 -1
  44. data/app/views/madmin/fields/json/_form.html.erb +1 -1
  45. data/app/views/madmin/fields/json/_show.html.erb +1 -1
  46. data/app/views/madmin/fields/nested_has_many/_index.html.erb +1 -1
  47. data/app/views/madmin/fields/nested_has_many/_show.html.erb +3 -11
  48. data/app/views/madmin/fields/nested_has_one/_form.html.erb +18 -0
  49. data/app/views/madmin/fields/polymorphic/_form.html.erb +1 -2
  50. data/app/views/madmin/fields/polymorphic/_index.html.erb +5 -1
  51. data/app/views/madmin/fields/polymorphic/_show.html.erb +1 -1
  52. data/app/views/madmin/fields/select/_form.html.erb +1 -1
  53. data/config/importmap.rb +0 -1
  54. data/config/locales/el.yml +86 -0
  55. data/config/locales/en.yml +46 -0
  56. data/config/locales/fr.yml +46 -0
  57. data/config/locales/ko.yml +84 -0
  58. data/lib/madmin/field.rb +27 -1
  59. data/lib/madmin/fields/array.rb +16 -0
  60. data/lib/madmin/fields/attachment.rb +7 -0
  61. data/lib/madmin/fields/belongs_to.rb +2 -1
  62. data/lib/madmin/fields/boolean.rb +1 -0
  63. data/lib/madmin/fields/currency.rb +7 -0
  64. data/lib/madmin/fields/date.rb +1 -0
  65. data/lib/madmin/fields/date_time.rb +1 -0
  66. data/lib/madmin/fields/decimal.rb +1 -0
  67. data/lib/madmin/fields/float.rb +1 -0
  68. data/lib/madmin/fields/has_many.rb +6 -23
  69. data/lib/madmin/fields/integer.rb +1 -0
  70. data/lib/madmin/fields/json.rb +21 -0
  71. data/lib/madmin/fields/nested_has_many.rb +2 -2
  72. data/lib/madmin/fields/nested_has_one.rb +48 -0
  73. data/lib/madmin/fields/polymorphic.rb +5 -0
  74. data/lib/madmin/fields/string.rb +3 -1
  75. data/lib/madmin/fields/text.rb +3 -1
  76. data/lib/madmin/filter.rb +96 -0
  77. data/lib/madmin/menu.rb +2 -1
  78. data/lib/madmin/page.rb +61 -0
  79. data/lib/madmin/pagination.rb +50 -0
  80. data/lib/madmin/resource.rb +95 -7
  81. data/lib/madmin/resource_builder.rb +8 -0
  82. data/lib/madmin/search.rb +5 -7
  83. data/lib/madmin/version.rb +1 -1
  84. data/lib/madmin.rb +47 -2
  85. metadata +19 -16
@@ -37,6 +37,13 @@ module Madmin
37
37
  scopes << name
38
38
  end
39
39
 
40
+ # Label for a scope button on the index. Looks up
41
+ # madmin.scopes.<param_key>.<name>, then madmin.scopes.<name> in I18n,
42
+ # falling back to the humanized scope name. Override for custom labels.
43
+ def scope_label(name)
44
+ I18n.t("madmin.scopes.#{param_key}.#{name}", default: [:"madmin.scopes.#{name}", name.to_s.humanize])
45
+ end
46
+
40
47
  def get_attribute(name)
41
48
  attributes[name]
42
49
  end
@@ -76,8 +83,21 @@ module Madmin
76
83
 
77
84
  # Returns singular name
78
85
  # For example: "Forum::Post" -> "Forum / Post"
86
+ #
87
+ # Uses the model's translation (`activerecord.models.forum/post`) when there is one
79
88
  def friendly_name
80
- model_name.split("::").map { |part| part.underscore.humanize }.join(" / ").titlecase
89
+ translated_name || model_name.split("::").map { |part| part.underscore.humanize }.join(" / ").titlecase
90
+ end
91
+
92
+ # Returns plural name
93
+ # For example: "Forum::Post" -> "Forum / Posts"
94
+ def friendly_plural_name
95
+ singular = translated_name
96
+ return friendly_name.pluralize unless singular
97
+
98
+ # A translation without `one` / `other` forms is the same for every count
99
+ return translated_name(count: 2) if plural_translation? || !locale_inflections?
100
+ singular.pluralize(I18n.locale)
81
101
  end
82
102
 
83
103
  # Support for isolated namespaces
@@ -91,19 +111,28 @@ module Madmin
91
111
  end
92
112
 
93
113
  def index_path(options = {})
94
- url_helpers.polymorphic_path([:madmin, route_namespace, model], options)
114
+ route_path([:madmin, route_namespace, model], options)
95
115
  end
96
116
 
97
117
  def new_path
98
- url_helpers.polymorphic_path([:madmin, route_namespace, model], action: :new)
118
+ route_path([:madmin, route_namespace, model], action: :new)
99
119
  end
100
120
 
101
121
  def show_path(record)
102
- url_helpers.polymorphic_path([:madmin, route_namespace, becomes(record)])
122
+ route_path([:madmin, route_namespace, becomes(record)])
103
123
  end
104
124
 
105
125
  def edit_path(record)
106
- url_helpers.polymorphic_path([:madmin, route_namespace, becomes(record)], action: :edit)
126
+ route_path([:madmin, route_namespace, becomes(record)], action: :edit)
127
+ end
128
+
129
+ # Whether a route is drawn for the given action on this resource's controller
130
+ # For example: PostResource.route?(:destroy)
131
+ def route?(action)
132
+ controller = "madmin/#{model.model_name.collection}"
133
+ Rails.application.routes.routes.any? do |route|
134
+ route.defaults[:controller] == controller && route.defaults[:action] == action.to_s
135
+ end
107
136
  end
108
137
 
109
138
  def becomes(record)
@@ -126,7 +155,15 @@ module Madmin
126
155
  model.respond_to? :friendly
127
156
  end
128
157
 
158
+ def readonly?
159
+ false
160
+ end
161
+
129
162
  def sortable_columns
163
+ model_column_names
164
+ end
165
+
166
+ def model_column_names
130
167
  model.column_names
131
168
  end
132
169
 
@@ -134,6 +171,10 @@ module Madmin
134
171
  attributes.values.select { |a| a.field.searchable? }
135
172
  end
136
173
 
174
+ def filterable_attributes
175
+ attributes.values.select { |a| a.field&.filter_type }
176
+ end
177
+
137
178
  def member_action(collection: false, &block)
138
179
  member_actions << MemberAction.new(collection: collection, &block)
139
180
  end
@@ -149,6 +190,7 @@ module Madmin
149
190
 
150
191
  def field_for_type(type)
151
192
  {
193
+ array: Fields::Array,
152
194
  binary: Fields::String,
153
195
  blob: Fields::Text,
154
196
  boolean: Fields::Boolean,
@@ -208,7 +250,8 @@ module Madmin
208
250
  has_many: Fields::HasMany,
209
251
  has_one: Fields::HasOne,
210
252
  rich_text: Fields::RichText,
211
- nested_has_many: Fields::NestedHasMany
253
+ nested_has_many: Fields::NestedHasMany,
254
+ nested_has_one: Fields::NestedHasOne
212
255
  }[type]
213
256
  end
214
257
 
@@ -219,6 +262,8 @@ module Madmin
219
262
  column_type = model.attribute_types[name_string]
220
263
  if column_type.is_a? ::ActiveRecord::Enum::EnumType
221
264
  :enum
265
+ elsif model.columns_hash[name_string].try(:array?)
266
+ :array
222
267
  else
223
268
  column_type.type || :string
224
269
  end
@@ -257,7 +302,29 @@ module Madmin
257
302
  @url_helpers ||= Rails.application.routes.url_helpers
258
303
  end
259
304
 
305
+ # Raises MissingRoute with instructions instead of a NoMethodError for
306
+ # the undefined route helper when the resource's routes aren't drawn
307
+ def route_path(components, options = {})
308
+ url_helpers.polymorphic_path(components, options)
309
+ rescue NoMethodError => error
310
+ raise unless error.name.to_s.end_with?("_path")
311
+
312
+ *namespaces, resources = model.model_name.collection.split("/")
313
+ routes = namespaces.reverse.inject("resources :#{resources}") do |body, namespace|
314
+ "namespace :#{namespace} do\n#{body.indent(2)}\nend"
315
+ end
316
+
317
+ raise MissingRoute, [
318
+ "`#{error.name}` is not defined, so #{name} has no route for this page.",
319
+ "Add the route inside `namespace :madmin` in your routes:",
320
+ routes.indent(4),
321
+ "To hide the resource from the menu instead, add `menu false` to #{name}."
322
+ ].join("\n\n")
323
+ end
324
+
260
325
  def model_store_accessors
326
+ return [] unless model.respond_to?(:stored_attributes)
327
+
261
328
  store_accessors = model.stored_attributes.values
262
329
  store_accessors.flatten
263
330
  end
@@ -269,7 +336,28 @@ module Madmin
269
336
  def menu_options
270
337
  return false if @menu_options == false
271
338
  @menu_options ||= {}
272
- @menu_options.with_defaults(label: friendly_name.pluralize, url: index_path)
339
+ # The default label follows the locale, so the menu tracks the item by resource instead
340
+ @menu_options.with_defaults(key: @menu_options[:label] || name, label: friendly_plural_name, url: index_path)
341
+ end
342
+
343
+ private
344
+
345
+ def translated_name(count: 1)
346
+ model.model_name.human(count: count, default: "").presence
347
+ end
348
+
349
+ # Whether the model's translation spells out its own `one` / `other` forms
350
+ def plural_translation?
351
+ model.lookup_ancestors.each do |klass|
352
+ translation = I18n.t(klass.model_name.i18n_key, scope: [klass.i18n_scope, :models], default: "")
353
+ return translation.is_a?(Hash) if translation.present?
354
+ end
355
+ false
356
+ end
357
+
358
+ # Locales without inflection rules of their own (most non-English ones) are left unpluralized
359
+ def locale_inflections?
360
+ ActiveSupport::Inflector::Inflections.instance(I18n.locale).plurals.any?
273
361
  end
274
362
  end
275
363
 
@@ -7,6 +7,8 @@ module Madmin
7
7
  end
8
8
 
9
9
  def associations
10
+ return [] unless model.respond_to?(:reflections)
11
+
10
12
  model.reflections.reject { |name, association|
11
13
  # Hide these special associations
12
14
  name.starts_with?("rich_text") ||
@@ -22,10 +24,14 @@ module Madmin
22
24
  end
23
25
 
24
26
  def store_accessors
27
+ return [] unless model.respond_to?(:stored_attributes)
28
+
25
29
  model.stored_attributes.values.flatten
26
30
  end
27
31
 
28
32
  def virtual_attributes
33
+ return [] unless model.respond_to?(:attribute_types)
34
+
29
35
  virtual = []
30
36
 
31
37
  # has_secure_password columns
@@ -50,6 +56,8 @@ module Madmin
50
56
  end
51
57
 
52
58
  def redundant_attributes
59
+ return [] unless model.respond_to?(:attribute_types)
60
+
53
61
  redundant = []
54
62
 
55
63
  # has_secure_password columns
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.5.1"
2
+ VERSION = "3.0.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"
@@ -78,18 +114,26 @@ module Madmin
78
114
  end
79
115
 
80
116
  def sti_resource_name_for(object)
117
+ return unless object.class.respond_to?(:inheritance_column) && object.class.respond_to?(:column_names)
118
+
81
119
  if (column = object.class.inheritance_column) && object.class.column_names.include?(column)
82
120
  "#{object.class.superclass.base_class.name}Resource"
83
121
  end
84
122
  end
85
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
86
126
  def resource_by_name(name)
87
127
  "#{name}Resource".constantize
88
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
+
89
133
  raise MissingResource, <<~MESSAGE
90
134
  #{name}Resource is missing. Create it by running:
91
135
 
92
- bin/rails generate madmin:resource #{resource_name.split("Resource").first}
136
+ bin/rails generate madmin:resource #{name}
93
137
  MESSAGE
94
138
  end
95
139
 
@@ -99,6 +143,7 @@ module Madmin
99
143
 
100
144
  def reset_resources!
101
145
  @resources = nil
146
+ @resources_by_model = nil
102
147
  menu.reset
103
148
  end
104
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.5.1
4
+ version: 3.0.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
@@ -198,8 +194,10 @@ files:
198
194
  - app/views/madmin/fields/time/_show.html.erb
199
195
  - app/views/madmin/shared/_label.html.erb
200
196
  - config/importmap.rb
197
+ - config/locales/el.yml
201
198
  - config/locales/en.yml
202
199
  - config/locales/fr.yml
200
+ - config/locales/ko.yml
203
201
  - lib/generators/madmin/field/field_generator.rb
204
202
  - lib/generators/madmin/field/templates/_form.html.erb
205
203
  - lib/generators/madmin/field/templates/_index.html.erb
@@ -223,6 +221,7 @@ files:
223
221
  - lib/madmin.rb
224
222
  - lib/madmin/engine.rb
225
223
  - lib/madmin/field.rb
224
+ - lib/madmin/fields/array.rb
226
225
  - lib/madmin/fields/attachment.rb
227
226
  - lib/madmin/fields/attachments.rb
228
227
  - lib/madmin/fields/belongs_to.rb
@@ -239,6 +238,7 @@ files:
239
238
  - lib/madmin/fields/integer.rb
240
239
  - lib/madmin/fields/json.rb
241
240
  - lib/madmin/fields/nested_has_many.rb
241
+ - lib/madmin/fields/nested_has_one.rb
242
242
  - lib/madmin/fields/password.rb
243
243
  - lib/madmin/fields/polymorphic.rb
244
244
  - lib/madmin/fields/rich_text.rb
@@ -246,10 +246,13 @@ files:
246
246
  - lib/madmin/fields/string.rb
247
247
  - lib/madmin/fields/text.rb
248
248
  - lib/madmin/fields/time.rb
249
+ - lib/madmin/filter.rb
249
250
  - lib/madmin/generator_helpers.rb
250
251
  - lib/madmin/member_action.rb
251
252
  - lib/madmin/menu.rb
252
253
  - lib/madmin/namespace.rb
254
+ - lib/madmin/page.rb
255
+ - lib/madmin/pagination.rb
253
256
  - lib/madmin/resource.rb
254
257
  - lib/madmin/resource_builder.rb
255
258
  - lib/madmin/search.rb
@@ -274,7 +277,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
274
277
  - !ruby/object:Gem::Version
275
278
  version: '0'
276
279
  requirements: []
277
- rubygems_version: 4.0.18
280
+ rubygems_version: 4.0.20
278
281
  specification_version: 4
279
282
  summary: A modern admin for Ruby on Rails apps
280
283
  test_files: []