administrate 0.19.0 → 1.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 (108) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +9 -9
  3. data/app/assets/builds/administrate/application.css +2614 -0
  4. data/app/assets/builds/administrate/application.css.map +1 -0
  5. data/app/assets/builds/administrate/application.js +31661 -0
  6. data/app/assets/builds/administrate/application.js.map +7 -0
  7. data/app/assets/builds/administrate-internal/docs.css +102 -0
  8. data/app/assets/builds/administrate-internal/docs.css.map +1 -0
  9. data/app/assets/config/administrate_manifest.js +2 -0
  10. data/app/assets/javascripts/administrate/add_jquery.js +4 -0
  11. data/app/assets/javascripts/administrate/application.js +9 -4
  12. data/app/assets/javascripts/administrate/controllers/application.js +9 -0
  13. data/app/assets/javascripts/administrate/controllers/index.js +9 -0
  14. data/app/assets/javascripts/administrate/controllers/select_controller.js +25 -0
  15. data/app/assets/javascripts/administrate/{components/table.js → controllers/table_controller.js} +9 -9
  16. data/app/assets/javascripts/administrate/controllers/tooltip_controller.js +24 -0
  17. data/app/assets/javascripts/administrate/vendor/css-anchor-positioning.js +9310 -0
  18. data/app/assets/stylesheets/administrate/application.scss +3 -3
  19. data/app/assets/stylesheets/administrate/base/_forms.scss +4 -4
  20. data/app/assets/stylesheets/administrate/base/_layout.scss +5 -0
  21. data/app/assets/stylesheets/administrate/base/_tables.scss +1 -1
  22. data/app/assets/stylesheets/administrate/base/_typography.scss +15 -1
  23. data/app/assets/stylesheets/administrate/components/_attributes.scss +1 -0
  24. data/app/assets/stylesheets/administrate/components/_buttons.scss +37 -12
  25. data/app/assets/stylesheets/administrate/components/_cells.scss +26 -19
  26. data/app/assets/stylesheets/administrate/components/_field-unit.scss +10 -3
  27. data/app/assets/stylesheets/administrate/components/_main-content.scss +1 -1
  28. data/app/assets/stylesheets/administrate/components/_navigation.scss +3 -3
  29. data/app/assets/stylesheets/administrate/components/_search.scss +55 -14
  30. data/app/assets/stylesheets/administrate/library/_variables.scss +7 -3
  31. data/app/assets/stylesheets/administrate/reset/_normalize.scss +7 -1
  32. data/app/assets/stylesheets/{docs.scss → administrate-internal/docs.scss} +25 -23
  33. data/app/controllers/administrate/application_controller.rb +28 -39
  34. data/app/controllers/concerns/administrate/punditize.rb +19 -31
  35. data/app/helpers/administrate/application_helper.rb +13 -5
  36. data/app/views/administrate/application/_collection.html.erb +30 -20
  37. data/app/views/administrate/application/_collection_header_actions.html.erb +1 -1
  38. data/app/views/administrate/application/_collection_item_actions.html.erb +4 -4
  39. data/app/views/administrate/application/_form.html.erb +20 -5
  40. data/app/views/administrate/application/_icons.html.erb +14 -6
  41. data/app/views/administrate/application/_index_header.html.erb +23 -0
  42. data/app/views/administrate/application/_javascript.html.erb +1 -1
  43. data/app/views/administrate/application/edit.html.erb +15 -3
  44. data/app/views/administrate/application/index.html.erb +20 -11
  45. data/app/views/administrate/application/new.html.erb +16 -4
  46. data/app/views/administrate/application/show.html.erb +37 -17
  47. data/app/views/fields/belongs_to/_form.html.erb +3 -2
  48. data/app/views/fields/has_many/_form.html.erb +2 -2
  49. data/app/views/fields/has_one/_form.html.erb +20 -4
  50. data/app/views/fields/has_one/_show.html.erb +18 -12
  51. data/app/views/fields/polymorphic/_form.html.erb +1 -1
  52. data/app/views/fields/rich_text/_form.html.erb +22 -0
  53. data/app/views/fields/rich_text/_index.html.erb +18 -0
  54. data/app/views/fields/rich_text/_show.html.erb +18 -0
  55. data/app/views/fields/select/_form.html.erb +2 -1
  56. data/app/views/fields/text/_form.html.erb +1 -1
  57. data/app/views/layouts/administrate/application.html.erb +1 -2
  58. data/docs/customizing_controller_actions.md +14 -0
  59. data/docs/customizing_dashboards.md +268 -12
  60. data/docs/customizing_page_views.md +48 -1
  61. data/docs/getting_started.md +1 -1
  62. data/docs/guides/scoping_has_many_relations.md +2 -2
  63. data/docs/guides/switching_templates_with_view_variants.md +45 -0
  64. data/docs/guides.md +1 -0
  65. data/docs/migrating-to-v1.md +34 -0
  66. data/lib/administrate/base_dashboard.rb +20 -10
  67. data/lib/administrate/engine.rb +7 -6
  68. data/lib/administrate/field/associative.rb +8 -23
  69. data/lib/administrate/field/base.rb +40 -5
  70. data/lib/administrate/field/belongs_to.rb +8 -8
  71. data/lib/administrate/field/date.rb +6 -2
  72. data/lib/administrate/field/date_time.rb +3 -4
  73. data/lib/administrate/field/deferred.rb +14 -18
  74. data/lib/administrate/field/has_many.rb +29 -6
  75. data/lib/administrate/field/has_one.rb +11 -15
  76. data/lib/administrate/field/number.rb +2 -8
  77. data/lib/administrate/field/password.rb +4 -0
  78. data/lib/administrate/field/polymorphic.rb +4 -4
  79. data/lib/administrate/field/rich_text.rb +21 -0
  80. data/lib/administrate/field/select.rb +4 -0
  81. data/lib/administrate/field/time.rb +5 -4
  82. data/lib/administrate/generator_helpers.rb +1 -1
  83. data/lib/administrate/namespace/resource.rb +1 -1
  84. data/lib/administrate/namespace.rb +10 -10
  85. data/lib/administrate/order.rb +40 -35
  86. data/lib/administrate/page/base.rb +2 -7
  87. data/lib/administrate/page/collection.rb +3 -2
  88. data/lib/administrate/page/form.rb +10 -2
  89. data/lib/administrate/page/show.rb +10 -2
  90. data/lib/administrate/resource_resolver.rb +1 -1
  91. data/lib/administrate/search.rb +14 -16
  92. data/lib/administrate/version.rb +1 -1
  93. data/lib/administrate/view_generator.rb +4 -3
  94. data/lib/administrate.rb +3 -36
  95. data/lib/generators/administrate/dashboard/dashboard_generator.rb +12 -9
  96. data/lib/generators/administrate/field/field_generator.rb +2 -2
  97. data/lib/generators/administrate/install/install_generator.rb +3 -2
  98. data/lib/generators/administrate/routes/routes_generator.rb +6 -5
  99. data/lib/generators/administrate/views/field_generator.rb +2 -2
  100. data/lib/generators/administrate/views/index_generator.rb +5 -0
  101. data/lib/generators/administrate/views/layout_generator.rb +1 -1
  102. metadata +54 -77
  103. data/app/assets/javascripts/administrate/components/associative.js +0 -5
  104. data/app/assets/javascripts/administrate/components/select.js +0 -3
  105. data/app/assets/stylesheets/administrate/utilities/_text-color.scss +0 -3
  106. data/lib/generators/administrate/assets/assets_generator.rb +0 -12
  107. data/lib/generators/administrate/assets/javascripts_generator.rb +0 -17
  108. data/lib/generators/administrate/assets/stylesheets_generator.rb +0 -17
@@ -10,18 +10,20 @@ module Administrate
10
10
  resources = order.apply(resources)
11
11
  resources = paginate_resources(resources)
12
12
  page = Administrate::Page::Collection.new(dashboard, order: order)
13
+ filters = Administrate::Search.new(scoped_resource, dashboard, search_term).valid_filters
13
14
 
14
15
  render locals: {
15
16
  resources: resources,
16
17
  search_term: search_term,
17
18
  page: page,
18
19
  show_search_bar: show_search_bar?,
20
+ filters: filters
19
21
  }
20
22
  end
21
23
 
22
24
  def show
23
25
  render locals: {
24
- page: Administrate::Page::Show.new(dashboard, requested_resource),
26
+ page: Administrate::Page::Show.new(dashboard, requested_resource)
25
27
  }
26
28
  end
27
29
 
@@ -29,13 +31,13 @@ module Administrate
29
31
  resource = new_resource
30
32
  authorize_resource(resource)
31
33
  render locals: {
32
- page: Administrate::Page::Form.new(dashboard, resource),
34
+ page: Administrate::Page::Form.new(dashboard, resource)
33
35
  }
34
36
  end
35
37
 
36
38
  def edit
37
39
  render locals: {
38
- page: Administrate::Page::Form.new(dashboard, requested_resource),
40
+ page: Administrate::Page::Form.new(dashboard, requested_resource)
39
41
  }
40
42
  end
41
43
 
@@ -44,13 +46,14 @@ module Administrate
44
46
  authorize_resource(resource)
45
47
 
46
48
  if resource.save
49
+ yield(resource) if block_given?
47
50
  redirect_to(
48
51
  after_resource_created_path(resource),
49
- notice: translate_with_resource("create.success"),
52
+ notice: translate_with_resource("create.success")
50
53
  )
51
54
  else
52
55
  render :new, locals: {
53
- page: Administrate::Page::Form.new(dashboard, resource),
56
+ page: Administrate::Page::Form.new(dashboard, resource)
54
57
  }, status: :unprocessable_entity
55
58
  end
56
59
  end
@@ -60,10 +63,11 @@ module Administrate
60
63
  redirect_to(
61
64
  after_resource_updated_path(requested_resource),
62
65
  notice: translate_with_resource("update.success"),
66
+ status: :see_other
63
67
  )
64
68
  else
65
69
  render :edit, locals: {
66
- page: Administrate::Page::Form.new(dashboard, requested_resource),
70
+ page: Administrate::Page::Form.new(dashboard, requested_resource)
67
71
  }, status: :unprocessable_entity
68
72
  end
69
73
  end
@@ -74,7 +78,7 @@ module Administrate
74
78
  else
75
79
  flash[:error] = requested_resource.errors.full_messages.join("<br/>")
76
80
  end
77
- redirect_to after_resource_destroyed_path(requested_resource)
81
+ redirect_to after_resource_destroyed_path(requested_resource), status: :see_other
78
82
  end
79
83
 
80
84
  private
@@ -83,12 +87,12 @@ module Administrate
83
87
  Administrate::Search.new(
84
88
  resources,
85
89
  dashboard,
86
- search_term,
90
+ search_term
87
91
  ).run
88
92
  end
89
93
 
90
94
  def after_resource_destroyed_path(_requested_resource)
91
- { action: :index }
95
+ {action: :index}
92
96
  end
93
97
 
94
98
  def after_resource_created_path(requested_resource)
@@ -102,7 +106,7 @@ module Administrate
102
106
  helper_method :nav_link_state
103
107
  def nav_link_state(resource)
104
108
  underscore_resource = resource.to_s.split("/").join("__")
105
- resource_name.to_s.pluralize == underscore_resource ? :active : :inactive
109
+ (resource_name.to_s.pluralize == underscore_resource) ? :active : :inactive
106
110
  end
107
111
 
108
112
  # Whether the named action route exists for the resource class.
@@ -118,15 +122,6 @@ module Administrate
118
122
  end
119
123
  helper_method :existing_action?
120
124
 
121
- # @deprecated Use {#existing_action} instead. Note that, in
122
- # {#existing_action}, the order of parameters is reversed and
123
- # there is no default value for the `resource` parameter.
124
- def valid_action?(action_name, resource = resource_class)
125
- Administrate.warn_of_deprecated_authorization_method(__method__)
126
- existing_action?(resource, action_name)
127
- end
128
- helper_method :valid_action?
129
-
130
125
  def routes
131
126
  @routes ||= Namespace.new(namespace).routes.to_set
132
127
  end
@@ -139,16 +134,18 @@ module Administrate
139
134
  @order ||= Administrate::Order.new(
140
135
  sorting_attribute,
141
136
  sorting_direction,
142
- association_attribute: order_by_field(
143
- dashboard_attribute(sorting_attribute),
144
- ),
137
+ sorting_column: sorting_column(
138
+ dashboard_attribute(sorting_attribute)
139
+ )
145
140
  )
146
141
  end
147
142
 
148
- def order_by_field(dashboard)
149
- return unless dashboard.try(:options)
143
+ def sorting_column(dashboard_attribute)
144
+ return unless dashboard_attribute.try(:options)
150
145
 
151
- dashboard.options.fetch(:order, nil)
146
+ dashboard_attribute.options.fetch(:sorting_column) {
147
+ dashboard_attribute.options.fetch(:order, nil)
148
+ }
152
149
  end
153
150
 
154
151
  def dashboard_attribute(attribute)
@@ -200,9 +197,9 @@ module Administrate
200
197
  end
201
198
 
202
199
  def resource_params
203
- params.require(resource_class.model_name.param_key).
204
- permit(dashboard.permitted_attributes(action_name)).
205
- transform_values { |v| read_param_value(v) }
200
+ params.require(resource_class.model_name.param_key)
201
+ .permit(dashboard.permitted_attributes(action_name))
202
+ .transform_values { |v| read_param_value(v) }
206
203
  end
207
204
 
208
205
  def read_param_value(data)
@@ -235,13 +232,13 @@ module Administrate
235
232
  def translate_with_resource(key)
236
233
  t(
237
234
  "administrate.controller.#{key}",
238
- resource: resource_resolver.resource_title,
235
+ resource: resource_resolver.resource_title
239
236
  )
240
237
  end
241
238
 
242
239
  def show_search_bar?
243
240
  dashboard.attribute_types_for(
244
- dashboard.all_attributes,
241
+ dashboard.all_attributes
245
242
  ).any? { |_name, attribute| attribute.searchable? }
246
243
  end
247
244
 
@@ -259,14 +256,6 @@ module Administrate
259
256
  end
260
257
  helper_method :authorized_action?
261
258
 
262
- # @deprecated Use {#authorized_action} instead. Note that the order of
263
- # parameters is reversed in {#authorized_action}.
264
- def show_action?(action, resource)
265
- Administrate.warn_of_deprecated_authorization_method(__method__)
266
- authorized_action?(resource, action)
267
- end
268
- helper_method :show_action?
269
-
270
259
  def new_resource(params = {})
271
260
  resource_class.new(params)
272
261
  end
@@ -278,7 +267,7 @@ module Administrate
278
267
  else
279
268
  raise Administrate::NotAuthorizedError.new(
280
269
  action: action_name,
281
- resource: resource,
270
+ resource: resource
282
271
  )
283
272
  end
284
273
  end
@@ -1,6 +1,6 @@
1
1
  module Administrate
2
2
  module Punditize
3
- if Object.const_defined?("Pundit")
3
+ if Object.const_defined?(:Pundit)
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  if Pundit.const_defined?(:Authorization)
@@ -9,31 +9,27 @@ module Administrate
9
9
  include Pundit
10
10
  end
11
11
 
12
- included do
13
- private
14
-
15
- def policy_namespace
16
- []
17
- end
12
+ private
18
13
 
19
- def scoped_resource
20
- namespaced_scope = policy_namespace + [super]
21
- policy_scope!(pundit_user, namespaced_scope)
22
- end
14
+ def policy_namespace
15
+ []
16
+ end
23
17
 
24
- def authorize_resource(resource)
25
- namespaced_resource = policy_namespace + [resource]
26
- authorize namespaced_resource
27
- end
18
+ def scoped_resource
19
+ namespaced_scope = policy_namespace + [super]
20
+ policy_scope!(pundit_user, namespaced_scope)
21
+ end
28
22
 
29
- def authorized_action?(resource, action)
30
- namespaced_resource = policy_namespace + [resource]
31
- policy = Pundit.policy!(pundit_user, namespaced_resource)
32
- policy.send("#{action}?".to_sym)
33
- end
23
+ def authorize_resource(resource)
24
+ namespaced_resource = policy_namespace + [resource]
25
+ authorize namespaced_resource
34
26
  end
35
27
 
36
- private
28
+ def authorized_action?(resource, action)
29
+ namespaced_resource = policy_namespace + [resource]
30
+ policy = Pundit.policy!(pundit_user, namespaced_resource)
31
+ policy.send(:"#{action}?")
32
+ end
37
33
 
38
34
  def policy_scope!(user, scope)
39
35
  policy_scope_class = Pundit::PolicyFinder.new(scope).scope!
@@ -42,18 +38,10 @@ module Administrate
42
38
  policy_scope = policy_scope_class.new(user, pundit_model(scope))
43
39
  rescue ArgumentError
44
40
  raise(Pundit::InvalidConstructorError,
45
- "Invalid #<#{policy_scope_class}> constructor is called")
41
+ "Invalid #<#{policy_scope_class}> constructor is called")
46
42
  end
47
43
 
48
- if policy_scope.respond_to? :resolve_admin
49
- ActiveSupport::Deprecation.warn(
50
- "Pundit policy scope `resolve_admin` method is deprecated. " +
51
- "Please use a namespaced pundit policy instead.",
52
- )
53
- policy_scope.resolve_admin
54
- else
55
- policy_scope.resolve
56
- end
44
+ policy_scope.resolve
57
45
  end
58
46
 
59
47
  def pundit_model(record)
@@ -9,7 +9,15 @@ module Administrate
9
9
 
10
10
  def render_field(field, locals = {})
11
11
  locals[:field] = field
12
- render locals: locals, partial: field.to_partial_path
12
+ if (prefix = find_partial_prefix(field))
13
+ render locals: locals, partial: "#{prefix}/#{field.page}"
14
+ end
15
+ end
16
+
17
+ def find_partial_prefix(field)
18
+ field.partial_prefixes.detect do |prefix|
19
+ lookup_context.template_exists?(field.page, [prefix], true)
20
+ end
13
21
  end
14
22
 
15
23
  def requireness(field)
@@ -50,7 +58,7 @@ module Administrate
50
58
  def display_resource_name(resource_name, opts = {})
51
59
  dashboard_from_resource(resource_name).resource_name(
52
60
  count: opts[:singular] ? SINGULAR_COUNT : PLURAL_MANY_COUNT,
53
- default: default_resource_name(resource_name, opts),
61
+ default: default_resource_name(resource_name, opts)
54
62
  )
55
63
  end
56
64
 
@@ -65,14 +73,14 @@ module Administrate
65
73
  def resource_index_route(resource_name)
66
74
  url_for(
67
75
  action: "index",
68
- controller: "/#{namespace}/#{resource_name}",
76
+ controller: "/#{namespace}/#{resource_name}"
69
77
  )
70
78
  end
71
79
 
72
80
  def sanitized_order_params(page, current_field_name)
73
81
  collection_names = page.item_associations + [current_field_name]
74
82
  association_params = collection_names.map do |assoc_name|
75
- { assoc_name => %i[order direction page per_page] }
83
+ {assoc_name => %i[order direction page per_page]}
76
84
  end
77
85
  params.permit(:search, :id, :_page, :per_page, association_params)
78
86
  end
@@ -87,7 +95,7 @@ module Administrate
87
95
 
88
96
  def default_resource_name(name, opts = {})
89
97
  resource_name = (opts[:singular] ? name.to_s : name.to_s.pluralize)
90
- resource_name.gsub("/", "_").titleize
98
+ resource_name.tr("/", "_").titleize
91
99
  end
92
100
  end
93
101
  end
@@ -18,30 +18,40 @@ to display a collection of resources in an HTML table.
18
18
  [1]: http://www.rubydoc.info/gems/administrate/Administrate/Page/Collection
19
19
  %>
20
20
 
21
- <table aria-labelledby="<%= table_title %>">
21
+ <table aria-labelledby="<%= table_title %>" data-controller="table" data-action="click->table#visitDataUrl keydown->table#visitDataUrl">
22
22
  <thead>
23
23
  <tr>
24
24
  <% collection_presenter.attribute_types.each do |attr_name, attr_type| %>
25
25
  <th class="cell-label
26
- cell-label--<%= attr_type.html_class %>
27
- cell-label--<%= collection_presenter.ordered_html_class(attr_name) %>
28
- cell-label--<%= "#{collection_presenter.resource_name}_#{attr_name}" %>"
29
- scope="col"
30
- aria-sort="<%= sort_order(collection_presenter.ordered_html_class(attr_name)) %>">
31
- <%= link_to(sanitized_order_params(page, collection_field_name).merge(
32
- collection_presenter.order_params_for(attr_name, key: collection_field_name)
33
- )) do %>
34
- <%= t(
35
- "helpers.label.#{collection_presenter.resource_name}.#{attr_name}",
36
- default: resource_class.human_attribute_name(attr_name).titleize,
37
- ) %>
38
- <% if collection_presenter.ordered_by?(attr_name) %>
39
- <span class="cell-label__sort-indicator cell-label__sort-indicator--<%= collection_presenter.ordered_html_class(attr_name) %>">
40
- <svg aria-hidden="true">
41
- <use xlink:href="#icon-up-caret" />
42
- </svg>
43
- </span>
26
+ cell-label--<%= attr_type.html_class %>
27
+ cell-label--<%= collection_presenter.ordered_html_class(attr_name) %>
28
+ cell-label--<%= "#{collection_presenter.resource_name}_#{attr_name}" %>"
29
+ scope="col"
30
+ <% if attr_type.sortable? %>
31
+ aria-sort="<%= sort_order(collection_presenter.ordered_html_class(attr_name)) %>"
32
+ <% end %>
33
+ >
34
+ <% if attr_type.sortable? %>
35
+ <%= link_to(params: sanitized_order_params(page, collection_field_name).merge(
36
+ collection_presenter.order_params_for(attr_name, key: collection_field_name)
37
+ )) do %>
38
+ <%= t(
39
+ "helpers.label.#{collection_presenter.resource_name}.#{attr_name}",
40
+ default: resource_class.human_attribute_name(attr_name).titleize,
41
+ ) %>
42
+ <% if collection_presenter.ordered_by?(attr_name) %>
43
+ <span class="cell-label__sort-indicator cell-label__sort-indicator--<%= collection_presenter.ordered_html_class(attr_name) %>">
44
+ <svg aria-hidden="true">
45
+ <use xlink:href="#icon-up-caret" />
46
+ </svg>
47
+ </span>
48
+ <% end %>
44
49
  <% end %>
50
+ <% else %>
51
+ <%= t(
52
+ "helpers.label.#{collection_presenter.resource_name}.#{attr_name}",
53
+ default: resource_class.human_attribute_name(attr_name).titleize,
54
+ ) %>
45
55
  <% end %>
46
56
  </th>
47
57
  <% end %>
@@ -59,7 +69,7 @@ to display a collection of resources in an HTML table.
59
69
  <% resources.each do |resource| %>
60
70
  <tr class="js-table-row"
61
71
  <% if accessible_action?(resource, :show) %>
62
- <%= %(tabindex=0 role=link data-url=#{polymorphic_path([namespace, resource])}) %>
72
+ <%= %(tabindex=0 data-url=#{polymorphic_path([namespace, resource])}) %>
63
73
  <% end %>
64
74
  >
65
75
  <% collection_presenter.attributes_for(resource).each do |attribute| %>
@@ -1,4 +1,4 @@
1
1
  <% [existing_action?(collection_presenter.resource_name, :edit),
2
2
  existing_action?(collection_presenter.resource_name, :destroy)].count(true).times do %>
3
- <th scope="col"></th>
3
+ <th scope="col" class="cell-label--action-button"></th>
4
4
  <% end %>
@@ -1,5 +1,5 @@
1
1
  <% if existing_action?(collection_presenter.resource_name, :edit) %>
2
- <td><%= link_to(
2
+ <td class="cell-label--action-button"><%= link_to(
3
3
  t("administrate.actions.edit"),
4
4
  [:edit, namespace, resource],
5
5
  class: "action-edit",
@@ -7,11 +7,11 @@
7
7
  <% end %>
8
8
 
9
9
  <% if existing_action?(collection_presenter.resource_name, :destroy) %>
10
- <td><%= link_to(
10
+ <td class="cell-label--action-button"><%= button_to(
11
11
  t("administrate.actions.destroy"),
12
12
  [namespace, resource],
13
- class: "text-color-red",
13
+ class: "link link--danger",
14
14
  method: :delete,
15
- data: { confirm: t("administrate.actions.confirm") }
15
+ data: { turbo_confirm: t("administrate.actions.confirm") }
16
16
  ) if accessible_action?(resource, :destroy) %></td>
17
17
  <% end %>
@@ -14,7 +14,7 @@ and renders all form fields for a resource's editable attributes.
14
14
  [1]: http://www.rubydoc.info/gems/administrate/Administrate/Page/Form
15
15
  %>
16
16
 
17
- <%= form_for([namespace, page.resource], html: { class: "form" }) do |f| %>
17
+ <%= form_with(model: page.resource, url: [namespace, page.resource], scope: page.resource, local: true, class: "form") do |f| %>
18
18
  <% if page.resource.errors.any? %>
19
19
  <div id="error_explanation">
20
20
  <h2>
@@ -33,10 +33,25 @@ and renders all form fields for a resource's editable attributes.
33
33
  </div>
34
34
  <% end %>
35
35
 
36
- <% page.attributes(controller.action_name).each do |attribute| -%>
37
- <div class="field-unit field-unit--<%= attribute.html_class %> field-unit--<%= requireness(attribute) %>">
38
- <%= render_field attribute, f: f %>
39
- </div>
36
+ <% page.attributes(controller.action_name).each do |title, attributes| -%>
37
+ <fieldset class="<%= "field-unit--nested" if title.present? %>">
38
+ <% if title.present? %>
39
+ <legend><%= t "helpers.label.#{f.object_name}.#{title}", default: title %></legend>
40
+ <% end %>
41
+
42
+ <% attributes.each do |attribute| %>
43
+ <div class="field-unit field-unit--<%= attribute.html_class %> field-unit--<%= requireness(attribute) %>">
44
+ <%= render_field attribute, f: f %>
45
+
46
+ <% hint_key = "administrate.field_hints.#{page.resource_name}.#{attribute.name}" %>
47
+ <% if I18n.exists?(hint_key) -%>
48
+ <div class="field-unit__hint">
49
+ <%= I18n.t(hint_key) %>
50
+ </div>
51
+ <% end -%>
52
+ </div>
53
+ <% end %>
54
+ </fieldset>
40
55
  <% end -%>
41
56
 
42
57
  <div class="form-actions">
@@ -1,13 +1,21 @@
1
1
  <svg hidden xmlns="http://www.w3.org/2000/svg">
2
- <symbol id="icon-cancel" viewBox="0 0 48 48">
3
- <path fill-rule="evenodd" d="M24 19.757l-8.485-8.485c-.784-.783-2.047-.782-2.827 0l-1.417 1.416c-.777.777-.78 2.046.002 2.827L19.757 24l-8.485 8.485c-.783.784-.782 2.047 0 2.827l1.416 1.417c.777.777 2.046.78 2.827-.002L24 28.243l8.485 8.485c.784.783 2.047.782 2.827 0l1.417-1.416c.777-.777.78-2.046-.002-2.827L28.243 24l8.485-8.485c.783-.784.782-2.047 0-2.827l-1.416-1.417c-.777-.777-2.046-.78-2.827.002L24 19.757zM24 47c12.703 0 23-10.297 23-23S36.703 1 24 1 1 11.297 1 24s10.297 23 23 23z" />
2
+ <symbol id="icon-cancel" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
3
+ <circle cx="12" cy="12" r="10"></circle>
4
+ <line x1="15" y1="9" x2="9" y2="15"></line><line x1="9" y1="9" x2="15" y2="15"></line>
4
5
  </symbol>
5
6
 
6
- <symbol id="icon-eyeglass" viewBox="0 0 48 48">
7
- <path d="M27.885 32.515c-2.864 1.966-6.333 3.116-10.07 3.116C7.976 35.63 0 27.656 0 17.817 0 7.976 7.976 0 17.816 0S35.63 7.976 35.63 17.816c0 3.736-1.15 7.205-3.115 10.07l14.53 14.53c1.278 1.277 1.275 3.352 0 4.628-1.28 1.278-3.353 1.278-4.63 0l-14.53-14.53zm-10.07-3.736c6.056 0 10.964-4.91 10.964-10.964 0-6.055-4.91-10.964-10.964-10.964-6.055 0-10.964 4.91-10.964 10.964 0 6.055 4.91 10.963 10.964 10.963z" />
7
+ <symbol id="icon-eyeglass" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
8
+ <circle cx="11" cy="11" r="8"></circle>
9
+ <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
8
10
  </symbol>
9
11
 
10
- <symbol id="icon-up-caret" viewBox="0 0 48 48">
11
- <path d="M2.988 33.02c-1.66 0-1.943-.81-.618-1.824l20-15.28c.878-.672 2.31-.67 3.188 0l20.075 15.288c1.316 1.003 1.048 1.816-.62 1.816H2.987z" />
12
+ <symbol id="icon-up-caret" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
13
+ <polyline points="18 15 12 9 6 15"></polyline>
12
14
  </symbol>
15
+
16
+ <symbol id="icon-question-mark" viewBox="0 0 24 24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
17
+ <circle cx="12" cy="12" r="10"></circle>
18
+ <path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path>
19
+ <line x1="12" y1="17" x2="12.01" y2="17"></line>
20
+ <symbol>
13
21
  </svg>
@@ -7,12 +7,33 @@
7
7
  <%= content_for(:title) %>
8
8
  </h1>
9
9
 
10
+ <%= content_for(:header_middle) %>
11
+
10
12
  <% if show_search_bar %>
11
13
  <%= render(
12
14
  "search",
13
15
  search_term: search_term,
14
16
  resource_name: display_resource_name(page.resource_name)
15
17
  ) %>
18
+
19
+ <% if filters.any? %>
20
+ <div data-controller="tooltip">
21
+ <button data-tooltip-target="tooltip" popovertarget="search-tooltip" class="button--tooltip search__tooltip">
22
+ <svg role="img">
23
+ <use xlink:href="#icon-question-mark" />
24
+ </svg>
25
+ </button>
26
+
27
+ <div data-tooltip-target="popover" popover id="search-tooltip" role="tooltip" class="search__tooltip-popover search__tooltip-popover-positioning">
28
+ <p><strong>Use filters to refine your search</strong></p>
29
+ <ul>
30
+ <% filters.keys.each do |filter_key| %>
31
+ <li><%= filter_key %>:<span class="search__tooltip-popover-value">&lt;value&gt;</span></li>
32
+ <% end %>
33
+ </ul>
34
+ </div>
35
+ </div>
36
+ <% end %>
16
37
  <% end %>
17
38
 
18
39
  <div>
@@ -25,4 +46,6 @@
25
46
  class: "button",
26
47
  ) if accessible_action?(new_resource, :new) %>
27
48
  </div>
49
+
50
+ <%= content_for(:header_last) %>
28
51
  </header>
@@ -8,7 +8,7 @@ by providing a `content_for(:javascript)` block.
8
8
  %>
9
9
 
10
10
  <% Administrate::Engine.javascripts.each do |js_path| %>
11
- <%= javascript_include_tag js_path %>
11
+ <%= javascript_include_tag js_path, "data-turbo-track": "reload", defer: true %>
12
12
  <% end %>
13
13
 
14
14
  <%= yield :javascript %>
@@ -22,6 +22,8 @@ It displays a header, and renders the `_form` partial to do the heavy lifting.
22
22
  <%= content_for(:title) %>
23
23
  </h1>
24
24
 
25
+ <%= content_for(:header_middle) %>
26
+
25
27
  <div>
26
28
  <%= link_to(
27
29
  t("administrate.actions.show_resource", name: page.page_title),
@@ -29,8 +31,18 @@ It displays a header, and renders the `_form` partial to do the heavy lifting.
29
31
  class: "button",
30
32
  ) if accessible_action?(page.resource, :show) %>
31
33
  </div>
34
+
35
+ <%= content_for(:header_last) %>
32
36
  </header>
33
37
 
34
- <section class="main-content__body">
35
- <%= render "form", page: page %>
36
- </section>
38
+ <%= content_for(:before_main) %>
39
+
40
+ <% if content_for?(:main) %>
41
+ <%= content_for(:main) %>
42
+ <% else %>
43
+ <section class="main-content__body">
44
+ <%= render "form", page: page %>
45
+ </section>
46
+ <% end %>
47
+
48
+ <%= content_for(:after_main) %>
@@ -29,18 +29,27 @@ It renders the `_table` partial to display details about the resources.
29
29
  search_term: search_term,
30
30
  page: page,
31
31
  show_search_bar: show_search_bar,
32
+ filters: filters,
32
33
  )
33
34
  %>
34
35
 
35
- <section class="main-content__body main-content__body--flush">
36
- <%= render(
37
- "collection",
38
- collection_presenter: page,
39
- collection_field_name: resource_name,
40
- page: page,
41
- resources: resources,
42
- table_title: "page-title"
43
- ) %>
36
+ <%= content_for(:before_main) %>
37
+
38
+ <% if content_for?(:main) %>
39
+ <%= content_for(:main) %>
40
+ <% else %>
41
+ <section class="main-content__body main-content__body--flush">
42
+ <%= render(
43
+ "collection",
44
+ collection_presenter: page,
45
+ collection_field_name: resource_name,
46
+ page: page,
47
+ resources: resources,
48
+ table_title: "page-title"
49
+ ) %>
50
+
51
+ <%= render("pagination", resources: resources) %>
52
+ </section>
53
+ <% end %>
44
54
 
45
- <%= render("pagination", resources: resources) %>
46
- </section>
55
+ <%= content_for(:after_main) %>
@@ -18,7 +18,7 @@ to do the heavy lifting.
18
18
  <% content_for(:title) do %>
19
19
  <%= t(
20
20
  "administrate.actions.new_resource",
21
- name: display_resource_name(page.resource_name).titleize
21
+ name: display_resource_name(page.resource_name, singular: true)
22
22
  ) %>
23
23
  <% end %>
24
24
 
@@ -27,11 +27,23 @@ to do the heavy lifting.
27
27
  <%= content_for(:title) %>
28
28
  </h1>
29
29
 
30
+ <%= content_for(:header_middle) %>
31
+
30
32
  <div>
31
33
  <%= link_to t("administrate.actions.back"), :back, class: "button" %>
32
34
  </div>
35
+
36
+ <%= content_for(:header_last) %>
33
37
  </header>
34
38
 
35
- <section class="main-content__body">
36
- <%= render "form", page: page %>
37
- </section>
39
+ <%= content_for(:before_main) %>
40
+
41
+ <% if content_for?(:main) %>
42
+ <%= content_for(:main) %>
43
+ <% else %>
44
+ <section class="main-content__body">
45
+ <%= render "form", page: page %>
46
+ </section>
47
+ <% end %>
48
+
49
+ <%= content_for(:after_main) %>