rails_admin_next 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.
- checksums.yaml +7 -0
- data/Gemfile +46 -0
- data/LICENSE.md +21 -0
- data/README.md +317 -0
- data/Rakefile +25 -0
- data/app/assets/javascripts/rails_admin/custom/ui.js +1 -0
- data/app/assets/stylesheets/rails_admin/framework.css +2375 -0
- data/app/assets/stylesheets/rails_admin/skin.css +472 -0
- data/app/assets/stylesheets/rails_admin/tokens.css +281 -0
- data/app/assets/stylesheets/rails_admin.css +47 -0
- data/app/controllers/rails_admin_next/application_controller.rb +98 -0
- data/app/controllers/rails_admin_next/main_controller.rb +185 -0
- data/app/helpers/rails_admin_next/application_helper.rb +254 -0
- data/app/helpers/rails_admin_next/form_builder.rb +173 -0
- data/app/helpers/rails_admin_next/main_helper.rb +63 -0
- data/app/views/layouts/rails_admin_next/_head.html.erb +29 -0
- data/app/views/layouts/rails_admin_next/_navigation.html.erb +18 -0
- data/app/views/layouts/rails_admin_next/_secondary_navigation.html.erb +27 -0
- data/app/views/layouts/rails_admin_next/_sidebar_navigation.html.erb +6 -0
- data/app/views/layouts/rails_admin_next/application.html.erb +42 -0
- data/app/views/layouts/rails_admin_next/content.html.erb +22 -0
- data/app/views/layouts/rails_admin_next/modal.js.erb +7 -0
- data/app/views/rails_admin_next/main/_dashboard_history.html.erb +46 -0
- data/app/views/rails_admin_next/main/_delete_notice.html.erb +36 -0
- data/app/views/rails_admin_next/main/_form_action_text.html.erb +2 -0
- data/app/views/rails_admin_next/main/_form_boolean.html.erb +15 -0
- data/app/views/rails_admin_next/main/_form_colorpicker.html.erb +6 -0
- data/app/views/rails_admin_next/main/_form_datetime.html.erb +14 -0
- data/app/views/rails_admin_next/main/_form_enumeration.html.erb +26 -0
- data/app/views/rails_admin_next/main/_form_field.html.erb +2 -0
- data/app/views/rails_admin_next/main/_form_file_upload.html.erb +18 -0
- data/app/views/rails_admin_next/main/_form_filtering_multiselect.html.erb +20 -0
- data/app/views/rails_admin_next/main/_form_filtering_select.html.erb +22 -0
- data/app/views/rails_admin_next/main/_form_multiple_file_upload.html.erb +21 -0
- data/app/views/rails_admin_next/main/_form_nested_many.html.erb +20 -0
- data/app/views/rails_admin_next/main/_form_nested_one.html.erb +19 -0
- data/app/views/rails_admin_next/main/_form_polymorphic_association.html.erb +24 -0
- data/app/views/rails_admin_next/main/_form_text.html.erb +2 -0
- data/app/views/rails_admin_next/main/_pagination.html.erb +54 -0
- data/app/views/rails_admin_next/main/_submit_buttons.html.erb +26 -0
- data/app/views/rails_admin_next/main/bulk_delete.html.erb +19 -0
- data/app/views/rails_admin_next/main/dashboard.html.erb +64 -0
- data/app/views/rails_admin_next/main/delete.html.erb +21 -0
- data/app/views/rails_admin_next/main/edit.html.erb +3 -0
- data/app/views/rails_admin_next/main/export.html.erb +149 -0
- data/app/views/rails_admin_next/main/history.html.erb +80 -0
- data/app/views/rails_admin_next/main/index.html.erb +186 -0
- data/app/views/rails_admin_next/main/new.html.erb +3 -0
- data/app/views/rails_admin_next/main/show.html.erb +30 -0
- data/config/brakeman.ignore +145 -0
- data/config/importmap.rails_admin.rb +29 -0
- data/config/initializers/active_record_extensions.rb +21 -0
- data/config/locales/rails_admin_next.en.yml +158 -0
- data/config/routes.rb +14 -0
- data/lib/generators/rails_admin_next/install_generator.rb +39 -0
- data/lib/generators/rails_admin_next/templates/initializer.erb +40 -0
- data/lib/generators/rails_admin_next/utils.rb +21 -0
- data/lib/rails_admin_next/abstract_model.rb +266 -0
- data/lib/rails_admin_next/adapters/active_record/association.rb +130 -0
- data/lib/rails_admin_next/adapters/active_record/object_extension.rb +13 -0
- data/lib/rails_admin_next/adapters/active_record/property.rb +58 -0
- data/lib/rails_admin_next/adapters/active_record.rb +371 -0
- data/lib/rails_admin_next/config/actions/base.rb +160 -0
- data/lib/rails_admin_next/config/actions/bulk_delete.rb +67 -0
- data/lib/rails_admin_next/config/actions/dashboard.rb +62 -0
- data/lib/rails_admin_next/config/actions/delete.rb +58 -0
- data/lib/rails_admin_next/config/actions/edit.rb +56 -0
- data/lib/rails_admin_next/config/actions/export.rb +47 -0
- data/lib/rails_admin_next/config/actions/history_index.rb +40 -0
- data/lib/rails_admin_next/config/actions/history_show.rb +40 -0
- data/lib/rails_admin_next/config/actions/index.rb +108 -0
- data/lib/rails_admin_next/config/actions/new.rb +69 -0
- data/lib/rails_admin_next/config/actions/show.rb +36 -0
- data/lib/rails_admin_next/config/actions/show_in_app.rb +37 -0
- data/lib/rails_admin_next/config/actions.rb +122 -0
- data/lib/rails_admin_next/config/configurable.rb +107 -0
- data/lib/rails_admin_next/config/const_load_suppressor.rb +78 -0
- data/lib/rails_admin_next/config/fields/association.rb +169 -0
- data/lib/rails_admin_next/config/fields/base.rb +391 -0
- data/lib/rails_admin_next/config/fields/collection_association.rb +90 -0
- data/lib/rails_admin_next/config/fields/factories/action_text.rb +14 -0
- data/lib/rails_admin_next/config/fields/factories/active_storage.rb +34 -0
- data/lib/rails_admin_next/config/fields/factories/association.rb +32 -0
- data/lib/rails_admin_next/config/fields/factories/encrypted_password.rb +28 -0
- data/lib/rails_admin_next/config/fields/factories/enum.rb +23 -0
- data/lib/rails_admin_next/config/fields/factories/password.rb +19 -0
- data/lib/rails_admin_next/config/fields/group.rb +77 -0
- data/lib/rails_admin_next/config/fields/singular_association.rb +62 -0
- data/lib/rails_admin_next/config/fields/types/action_text.rb +20 -0
- data/lib/rails_admin_next/config/fields/types/active_record_enum.rb +62 -0
- data/lib/rails_admin_next/config/fields/types/active_storage.rb +70 -0
- data/lib/rails_admin_next/config/fields/types/all.rb +32 -0
- data/lib/rails_admin_next/config/fields/types/belongs_to_association.rb +48 -0
- data/lib/rails_admin_next/config/fields/types/boolean.rb +70 -0
- data/lib/rails_admin_next/config/fields/types/citext.rb +15 -0
- data/lib/rails_admin_next/config/fields/types/color.rb +43 -0
- data/lib/rails_admin_next/config/fields/types/date.rb +38 -0
- data/lib/rails_admin_next/config/fields/types/datetime.rb +79 -0
- data/lib/rails_admin_next/config/fields/types/decimal.rb +23 -0
- data/lib/rails_admin_next/config/fields/types/enum.rb +52 -0
- data/lib/rails_admin_next/config/fields/types/file_upload.rb +87 -0
- data/lib/rails_admin_next/config/fields/types/float.rb +23 -0
- data/lib/rails_admin_next/config/fields/types/has_and_belongs_to_many_association.rb +16 -0
- data/lib/rails_admin_next/config/fields/types/has_many_association.rb +16 -0
- data/lib/rails_admin_next/config/fields/types/has_one_association.rb +35 -0
- data/lib/rails_admin_next/config/fields/types/hidden.rb +31 -0
- data/lib/rails_admin_next/config/fields/types/inet.rb +15 -0
- data/lib/rails_admin_next/config/fields/types/integer.rb +20 -0
- data/lib/rails_admin_next/config/fields/types/json.rb +37 -0
- data/lib/rails_admin_next/config/fields/types/multiple_active_storage.rb +82 -0
- data/lib/rails_admin_next/config/fields/types/multiple_file_upload.rb +134 -0
- data/lib/rails_admin_next/config/fields/types/numeric.rb +24 -0
- data/lib/rails_admin_next/config/fields/types/password.rb +45 -0
- data/lib/rails_admin_next/config/fields/types/polymorphic_association.rb +118 -0
- data/lib/rails_admin_next/config/fields/types/serialized.rb +28 -0
- data/lib/rails_admin_next/config/fields/types/string.rb +50 -0
- data/lib/rails_admin_next/config/fields/types/string_like.rb +25 -0
- data/lib/rails_admin_next/config/fields/types/text.rb +28 -0
- data/lib/rails_admin_next/config/fields/types/time.rb +39 -0
- data/lib/rails_admin_next/config/fields/types/timestamp.rb +16 -0
- data/lib/rails_admin_next/config/fields/types/uuid.rb +15 -0
- data/lib/rails_admin_next/config/fields/types.rb +29 -0
- data/lib/rails_admin_next/config/fields.rb +88 -0
- data/lib/rails_admin_next/config/groupable.rb +20 -0
- data/lib/rails_admin_next/config/has_description.rb +14 -0
- data/lib/rails_admin_next/config/has_fields.rb +144 -0
- data/lib/rails_admin_next/config/has_groups.rb +27 -0
- data/lib/rails_admin_next/config/hideable.rb +30 -0
- data/lib/rails_admin_next/config/inspectable.rb +39 -0
- data/lib/rails_admin_next/config/lazy_model.rb +74 -0
- data/lib/rails_admin_next/config/model.rb +126 -0
- data/lib/rails_admin_next/config/proxyable/proxy.rb +42 -0
- data/lib/rails_admin_next/config/proxyable.rb +26 -0
- data/lib/rails_admin_next/config/sections/base.rb +36 -0
- data/lib/rails_admin_next/config/sections/create.rb +13 -0
- data/lib/rails_admin_next/config/sections/edit.rb +13 -0
- data/lib/rails_admin_next/config/sections/export.rb +13 -0
- data/lib/rails_admin_next/config/sections/list.rb +63 -0
- data/lib/rails_admin_next/config/sections/modal.rb +12 -0
- data/lib/rails_admin_next/config/sections/nested.rb +12 -0
- data/lib/rails_admin_next/config/sections/show.rb +12 -0
- data/lib/rails_admin_next/config/sections/update.rb +12 -0
- data/lib/rails_admin_next/config/sections.rb +39 -0
- data/lib/rails_admin_next/config.rb +436 -0
- data/lib/rails_admin_next/engine.rb +91 -0
- data/lib/rails_admin_next/extension.rb +35 -0
- data/lib/rails_admin_next/extensions/cancancan/authorization_adapter.rb +90 -0
- data/lib/rails_admin_next/extensions/cancancan.rb +5 -0
- data/lib/rails_admin_next/extensions/controller_extension.rb +8 -0
- data/lib/rails_admin_next/extensions/paper_trail/auditing_adapter.rb +192 -0
- data/lib/rails_admin_next/extensions/paper_trail.rb +5 -0
- data/lib/rails_admin_next/extensions/pundit/authorization_adapter.rb +72 -0
- data/lib/rails_admin_next/extensions/pundit.rb +5 -0
- data/lib/rails_admin_next/extensions/url_for_extension.rb +15 -0
- data/lib/rails_admin_next/icons.rb +143 -0
- data/lib/rails_admin_next/support/composite_keys_serializer.rb +15 -0
- data/lib/rails_admin_next/support/csv_converter.rb +100 -0
- data/lib/rails_admin_next/support/hash_helper.rb +31 -0
- data/lib/rails_admin_next/support/paginated_collection.rb +79 -0
- data/lib/rails_admin_next/version.rb +17 -0
- data/lib/rails_admin_next.rb +72 -0
- data/lib/tasks/rails_admin.rake +40 -0
- data/src/rails_admin/application.js +15 -0
- data/src/rails_admin/base.js +12 -0
- data/src/rails_admin/controllers/alert_controller.js +28 -0
- data/src/rails_admin/controllers/collapse_controller.js +16 -0
- data/src/rails_admin/controllers/dropdown_controller.js +110 -0
- data/src/rails_admin/controllers/feedback_controller.js +73 -0
- data/src/rails_admin/controllers/filter_box_controller.js +346 -0
- data/src/rails_admin/controllers/filtering_multiselect_controller.js +386 -0
- data/src/rails_admin/controllers/filtering_select_controller.js +509 -0
- data/src/rails_admin/controllers/index.js +31 -0
- data/src/rails_admin/controllers/modal_controller.js +142 -0
- data/src/rails_admin/controllers/nested_form_controller.js +117 -0
- data/src/rails_admin/controllers/polymorphic_controller.js +26 -0
- data/src/rails_admin/controllers/remote_form_controller.js +173 -0
- data/src/rails_admin/controllers/sidescroll_controller.js +23 -0
- data/src/rails_admin/dom-ready.js +20 -0
- data/src/rails_admin/dom.js +19 -0
- data/src/rails_admin/i18n.js +20 -0
- data/src/rails_admin/icons.js +64 -0
- data/src/rails_admin/interactions.js +221 -0
- data/src/rails_admin/select_query.js +65 -0
- metadata +346 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RailsAdminNext
|
|
4
|
+
module ApplicationHelper
|
|
5
|
+
def authorized?(action_name, abstract_model = nil, object = nil)
|
|
6
|
+
object = nil if object.try :new_record?
|
|
7
|
+
action(action_name, abstract_model, object).try(:authorized?)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def current_action
|
|
11
|
+
params[:action].in?(%w[create new]) ? "create" : "update"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def current_action?(action, abstract_model = @abstract_model, object = @object)
|
|
15
|
+
@action.custom_key == action.custom_key &&
|
|
16
|
+
abstract_model.try(:to_param) == @abstract_model.try(:to_param) &&
|
|
17
|
+
(@object.try(:persisted?) ? @object.id == object.try(:id) : !object.try(:persisted?))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def action(key, abstract_model = nil, object = nil)
|
|
21
|
+
RailsAdminNext::Config::Actions.find(key, controller: controller, abstract_model: abstract_model, object: object)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Render an engine icon as inline SVG. `name` is a logical icon name (see
|
|
25
|
+
# RailsAdminNext::Icons::GLYPHS), e.g. :edit, :delete, :list. A nil/unknown
|
|
26
|
+
# name renders nothing rather than raising; a legacy Font Awesome class string
|
|
27
|
+
# is resolved best-effort and warns via RailsAdminNext.deprecator.
|
|
28
|
+
def rails_admin_icon(name, **html_options)
|
|
29
|
+
RailsAdminNext::Icons.svg(name, **html_options)&.html_safe || "".html_safe
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def actions(scope = :all, abstract_model = nil, object = nil)
|
|
33
|
+
RailsAdminNext::Config::Actions.all(scope, controller: controller, abstract_model: abstract_model, object: object)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def edit_user_link
|
|
37
|
+
return nil unless _current_user.try(:email).present?
|
|
38
|
+
return nil unless (abstract_model = RailsAdminNext.config(_current_user.class).abstract_model)
|
|
39
|
+
|
|
40
|
+
edit_action = action(:edit, abstract_model, _current_user)
|
|
41
|
+
authorized = edit_action.try(:authorized?)
|
|
42
|
+
content = edit_user_link_label
|
|
43
|
+
|
|
44
|
+
if authorized
|
|
45
|
+
edit_url = rails_admin_next.url_for(
|
|
46
|
+
action: edit_action.action_name,
|
|
47
|
+
model_name: abstract_model.to_param,
|
|
48
|
+
controller: "rails_admin_next/main",
|
|
49
|
+
id: _current_user.id
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
link_to content, edit_url, class: "nav-link"
|
|
53
|
+
else
|
|
54
|
+
content_tag :span, content, class: "nav-link"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def logout_path
|
|
59
|
+
main_app.logout_path if main_app.respond_to?(:logout_path)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def logout_method
|
|
63
|
+
:delete
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def wording_for(label, action = @action, abstract_model = @abstract_model, object = @object)
|
|
67
|
+
model_config = abstract_model.try(:config)
|
|
68
|
+
object = nil unless abstract_model && object.is_a?(abstract_model.model)
|
|
69
|
+
action = RailsAdminNext::Config::Actions.find(action.to_sym) if action.is_a?(Symbol) || action.is_a?(String)
|
|
70
|
+
|
|
71
|
+
I18n.t(
|
|
72
|
+
"admin.actions.#{action.i18n_key}.#{label}",
|
|
73
|
+
model_label: model_config&.label,
|
|
74
|
+
model_label_plural: model_config&.label_plural,
|
|
75
|
+
object_label: model_config && object.try(model_config.object_label_method)
|
|
76
|
+
)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def main_navigation
|
|
80
|
+
nodes_stack = RailsAdminNext::Config.visible_models(controller: controller)
|
|
81
|
+
node_model_names = nodes_stack.collect { |c| c.abstract_model.model_name }
|
|
82
|
+
parent_groups = nodes_stack.group_by { |n| n.parent&.to_s }
|
|
83
|
+
|
|
84
|
+
nodes_stack.group_by(&:navigation_label).collect do |navigation_label, nodes|
|
|
85
|
+
nodes = nodes.select { |n| n.parent.nil? || !n.parent.to_s.in?(node_model_names) }
|
|
86
|
+
li_stack = navigation parent_groups, nodes
|
|
87
|
+
|
|
88
|
+
label = navigation_label || t("admin.misc.navigation")
|
|
89
|
+
|
|
90
|
+
collapsible_stack(label, "main", li_stack)
|
|
91
|
+
end.join.html_safe
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def root_navigation
|
|
95
|
+
actions(:root).select(&:show_in_sidebar).group_by(&:sidebar_label).collect do |label, nodes|
|
|
96
|
+
li_stack = nodes.map do |node|
|
|
97
|
+
url = rails_admin_next.url_for(action: node.action_name, controller: "rails_admin_next/main")
|
|
98
|
+
nav_icon = rails_admin_icon(node.link_icon)
|
|
99
|
+
content_tag :li do
|
|
100
|
+
link_to nav_icon + " " + wording_for(:menu, node), url, class: "nav-link"
|
|
101
|
+
end
|
|
102
|
+
end.join.html_safe
|
|
103
|
+
label ||= t("admin.misc.root_navigation")
|
|
104
|
+
|
|
105
|
+
collapsible_stack(label, "action", li_stack)
|
|
106
|
+
end.join.html_safe
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def static_navigation
|
|
110
|
+
li_stack = RailsAdminNext::Config.navigation_static_links.collect do |title, url|
|
|
111
|
+
content_tag(:li, link_to(title.to_s, url, target: "_blank", rel: "noopener noreferrer", class: "nav-link"))
|
|
112
|
+
end.join.html_safe
|
|
113
|
+
|
|
114
|
+
label = RailsAdminNext::Config.navigation_static_label || t("admin.misc.navigation_static_label")
|
|
115
|
+
collapsible_stack(label, "static", li_stack) || ""
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def navigation(parent_groups, nodes, level = 0)
|
|
119
|
+
nodes.collect do |node|
|
|
120
|
+
abstract_model = node.abstract_model
|
|
121
|
+
model_param = abstract_model.to_param
|
|
122
|
+
url = rails_admin_next.url_for(action: :index, controller: "rails_admin_next/main", model_name: model_param)
|
|
123
|
+
nav_icon = rails_admin_icon(node.navigation_icon)
|
|
124
|
+
css_classes = ["nav-link"]
|
|
125
|
+
css_classes.push("nav-level-#{level}") if level > 0
|
|
126
|
+
css_classes.push("active") if @action && current_action?(@action, model_param)
|
|
127
|
+
li = content_tag :li, data: {model: model_param} do
|
|
128
|
+
link_to nav_icon + " " + node.label_plural, url, class: css_classes.join(" ")
|
|
129
|
+
end
|
|
130
|
+
child_nodes = parent_groups[abstract_model.model_name]
|
|
131
|
+
child_nodes ? li + navigation(parent_groups, child_nodes, level + 1) : li
|
|
132
|
+
end.join.html_safe
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def breadcrumb(action = @action, _acc = [])
|
|
136
|
+
begin
|
|
137
|
+
(parent_actions ||= []) << action
|
|
138
|
+
end while action.breadcrumb_parent && (action = action(*action.breadcrumb_parent)) # rubocop:disable Lint/Loop
|
|
139
|
+
|
|
140
|
+
content_tag(:ol, class: "breadcrumb") do
|
|
141
|
+
parent_actions.collect do |a|
|
|
142
|
+
am = a.send(:eval, "bindings[:abstract_model]")
|
|
143
|
+
o = a.send(:eval, "bindings[:object]")
|
|
144
|
+
content_tag(:li, class: ["breadcrumb-item", current_action?(a, am, o) && "active"]) do
|
|
145
|
+
if current_action?(a, am, o)
|
|
146
|
+
wording_for(:breadcrumb, a, am, o)
|
|
147
|
+
elsif a.http_methods.include?(:get)
|
|
148
|
+
link_to rails_admin_next.url_for(action: a.action_name, controller: "rails_admin_next/main", model_name: am.try(:to_param), id: o.try(:persisted?) && o.try(:id) || nil) do
|
|
149
|
+
wording_for(:breadcrumb, a, am, o)
|
|
150
|
+
end
|
|
151
|
+
else
|
|
152
|
+
content_tag(:span, wording_for(:breadcrumb, a, am, o))
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end.reverse.join.html_safe
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# parent => :root, :collection, :member
|
|
160
|
+
# perf matters here (no action view trickery)
|
|
161
|
+
def menu_for(parent, abstract_model = nil, object = nil, only_icon = false)
|
|
162
|
+
actions = actions(parent, abstract_model, object).select { |a| a.http_methods.include?(:get) && a.show_in_menu }
|
|
163
|
+
actions.collect do |action|
|
|
164
|
+
wording = wording_for(:menu, action)
|
|
165
|
+
li_class = ["nav-item", "icon", "#{action.key}_#{parent}_link"]
|
|
166
|
+
.concat(action.enabled? ? [] : ["disabled"])
|
|
167
|
+
content_tag(:li, {class: li_class}.merge(only_icon ? {title: wording, rel: "tooltip"} : {})) do
|
|
168
|
+
label = rails_admin_icon(action.link_icon) + " " + content_tag(:span, wording, (only_icon ? {style: "display:none"} : {}))
|
|
169
|
+
if action.enabled? || !only_icon
|
|
170
|
+
href =
|
|
171
|
+
if action.enabled?
|
|
172
|
+
rails_admin_next.url_for(action: action.action_name, controller: "rails_admin_next/main", model_name: abstract_model.try(:to_param), id: object.try(:persisted?) && object.try(:id) || nil)
|
|
173
|
+
else
|
|
174
|
+
"javascript:void(0)"
|
|
175
|
+
end
|
|
176
|
+
content_tag(:a, label, {href: href, target: action.link_target, class: ["nav-link", current_action?(action) && "active", !action.enabled? && "disabled"].compact}.merge(action.turbo? ? {} : {data: {turbo: "false"}}))
|
|
177
|
+
else
|
|
178
|
+
content_tag(:span, label)
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end.join(" ").html_safe
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def bulk_menu(abstract_model = @abstract_model)
|
|
185
|
+
actions = actions(:bulkable, abstract_model)
|
|
186
|
+
return "" if actions.empty?
|
|
187
|
+
|
|
188
|
+
content_tag :li, class: "nav-item dropdown" do
|
|
189
|
+
content_tag(:button, type: "button", id: "bulk_menu_toggle", class: "nav-link dropdown-toggle", popovertarget: "bulk_menu", data: {controller: "dropdown", action: "keydown->dropdown#onKeydown"}) { t("admin.misc.bulk_menu_title").html_safe + " " + '<b class="caret"></b>'.html_safe } +
|
|
190
|
+
content_tag(:ul, id: "bulk_menu", class: "dropdown-menu dropdown-menu-end", role: "menu", popover: "auto") do
|
|
191
|
+
actions.collect do |action|
|
|
192
|
+
content_tag :li, role: "none" do
|
|
193
|
+
link_to wording_for(:bulk_link, action, abstract_model), "#", class: "dropdown-item bulk-link", role: "menuitem", tabindex: "-1", data: {action: action.action_name}
|
|
194
|
+
end
|
|
195
|
+
end.join.html_safe
|
|
196
|
+
end
|
|
197
|
+
end.html_safe
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def flash_alert_class(flash_key)
|
|
201
|
+
case flash_key.to_s
|
|
202
|
+
when "error" then "alert-danger"
|
|
203
|
+
when "alert" then "alert-warning"
|
|
204
|
+
when "notice" then "alert-info"
|
|
205
|
+
else "alert-#{flash_key}"
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Workaround for https://github.com/rails/rails/issues/31325
|
|
210
|
+
def image_tag(source, options = {})
|
|
211
|
+
if %w[ActiveStorage::Variant ActiveStorage::VariantWithRecord ActiveStorage::Preview].include? source.class.to_s
|
|
212
|
+
super(main_app.route_for(ActiveStorage.resolve_model_to_route, source), options)
|
|
213
|
+
else
|
|
214
|
+
super
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
private
|
|
219
|
+
|
|
220
|
+
def edit_user_link_label
|
|
221
|
+
[
|
|
222
|
+
RailsAdminNext::Config.show_gravatar &&
|
|
223
|
+
image_tag(gravatar_url(_current_user.email), alt: ""),
|
|
224
|
+
|
|
225
|
+
content_tag(:span, _current_user.email)
|
|
226
|
+
].filter(&:present?).join.html_safe
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def gravatar_url(email)
|
|
230
|
+
"https://secure.gravatar.com/avatar/#{Digest::MD5.hexdigest email}?s=30"
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# Native disclosure: the group renders open and the browser owns toggling.
|
|
234
|
+
# The MD5-derived key is stable across renders, so a `turbo:before-render`
|
|
235
|
+
# hook (src/rails_admin/interactions.js) can carry a collapsed group's state
|
|
236
|
+
# into the next page. Find-in-page may auto-expand a collapsed group; that's
|
|
237
|
+
# accepted <details> platform behavior.
|
|
238
|
+
def collapsible_stack(label, class_prefix, li_stack)
|
|
239
|
+
return nil unless li_stack.present?
|
|
240
|
+
|
|
241
|
+
collapse_key = "#{class_prefix}-#{Digest::MD5.hexdigest(label)[0..7]}"
|
|
242
|
+
content_tag(:li, class: "mb-1") do
|
|
243
|
+
content_tag(:details, open: true, data: {collapse_key: collapse_key}) do
|
|
244
|
+
content_tag(:summary, class: "btn btn-toggle align-items-center rounded") do
|
|
245
|
+
rails_admin_icon(:collapse) + html_escape(" " + label)
|
|
246
|
+
end +
|
|
247
|
+
content_tag(:ul, class: "btn-toggle-nav list-unstyled fw-normal pb-1") do
|
|
248
|
+
li_stack
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
end
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RailsAdminNext
|
|
4
|
+
class FormBuilder < ::ActionView::Helpers::FormBuilder
|
|
5
|
+
include ::RailsAdminNext::ApplicationHelper
|
|
6
|
+
|
|
7
|
+
def generate(options = {})
|
|
8
|
+
without_field_error_proc_added_div do
|
|
9
|
+
options.reverse_merge!(
|
|
10
|
+
action: @template.controller.params[:action],
|
|
11
|
+
model_config: @template.instance_variable_get(:@model_config),
|
|
12
|
+
nested_in: false
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
object_infos +
|
|
16
|
+
visible_groups(options[:model_config], generator_action(options[:action], options[:nested_in])).collect do |fieldset|
|
|
17
|
+
fieldset_for fieldset, options[:nested_in]
|
|
18
|
+
end.join.html_safe +
|
|
19
|
+
(options[:nested_in] ? "" : @template.render(partial: "rails_admin_next/main/submit_buttons"))
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def fieldset_for(fieldset, nested_in)
|
|
24
|
+
fields = fieldset.with(
|
|
25
|
+
form: self,
|
|
26
|
+
object: @object,
|
|
27
|
+
view: @template,
|
|
28
|
+
controller: @template.controller
|
|
29
|
+
).visible_fields
|
|
30
|
+
return if fields.empty?
|
|
31
|
+
|
|
32
|
+
@template.content_tag :fieldset do
|
|
33
|
+
contents = []
|
|
34
|
+
chevron = RailsAdminNext::Icons.svg(:collapse, class: ("collapsed" unless fieldset.active?)).html_safe
|
|
35
|
+
legend = @template.safe_join([chevron, " #{fieldset.label}"])
|
|
36
|
+
contents << @template.content_tag(:legend, legend, style: (fieldset.name == :default) ? "display:none" : "")
|
|
37
|
+
contents << @template.content_tag(:p, fieldset.help) if fieldset.help.present?
|
|
38
|
+
contents << fields.collect { |field| field_wrapper_for(field, nested_in) }.join
|
|
39
|
+
contents.join.html_safe
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def field_wrapper_for(field, nested_in)
|
|
44
|
+
# do not show nested field if the target is the origin
|
|
45
|
+
return if nested_field_association?(field, nested_in)
|
|
46
|
+
|
|
47
|
+
@template.content_tag(:div, class: "control-group row mb-3 #{field.type_css_class} #{field.css_class} #{"error" if field.errors.present?}", id: "#{dom_id(field)}_field") do
|
|
48
|
+
if field.label
|
|
49
|
+
label(field.method_name, field.label, class: "col-sm-2 col-form-label text-md-end") +
|
|
50
|
+
(field.nested_form ? field_for(field) : input_for(field))
|
|
51
|
+
else
|
|
52
|
+
field.nested_form ? field_for(field) : input_for(field)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def input_for(field)
|
|
58
|
+
css = "col-sm-10 controls"
|
|
59
|
+
css += " has-error" if field.errors.present?
|
|
60
|
+
@template.content_tag(:div, class: css) do
|
|
61
|
+
field_for(field) +
|
|
62
|
+
errors_for(field) +
|
|
63
|
+
help_for(field)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def errors_for(field)
|
|
68
|
+
field.errors.present? ? @template.content_tag(:span, field.errors.to_sentence, class: "help-inline text-danger") : "".html_safe
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def help_for(field)
|
|
72
|
+
field.help.present? ? @template.content_tag(:div, field.help, class: "form-text") : "".html_safe
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def field_for(field)
|
|
76
|
+
field.read_only? ? @template.content_tag(:div, field.pretty_value, class: "form-control-static") : field.render
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Wraps one nested-attributes subform (rendered by native `fields_for`) for
|
|
80
|
+
# the nested-form Stimulus controller: a `.fields` container, an optional
|
|
81
|
+
# `_destroy` hidden field + remove button (gated on `allow_destroy`, always
|
|
82
|
+
# present in the blank <template>), then the recursively generated fields.
|
|
83
|
+
def nested_field_set(nested_form, field, allow_destroy:)
|
|
84
|
+
@template.content_tag(:div, class: "fields tab-pane active", data: {"nested-form-target": "subform"}) do
|
|
85
|
+
contents = []
|
|
86
|
+
# Keep the persisted-record id inside the `.fields` wrapper (Rails would
|
|
87
|
+
# otherwise append it as a sibling); the controller reads it to tell a
|
|
88
|
+
# persisted subform from a brand-new one. Suppress the auto-id in the
|
|
89
|
+
# partials with `include_id: false` so it is not rendered twice.
|
|
90
|
+
contents << nested_form.hidden_field(:id) if nested_form.object.persisted?
|
|
91
|
+
if allow_destroy
|
|
92
|
+
contents << nested_form.hidden_field(:_destroy, data: {"nested-form-target": "destroy"})
|
|
93
|
+
contents << @template.content_tag(:button, RailsAdminNext::Icons.svg(:trash).html_safe, type: "button", class: "btn btn-danger remove_nested_fields", data: {action: "nested-form#remove"})
|
|
94
|
+
end
|
|
95
|
+
contents << nested_form.generate(action: :nested, model_config: field.associated_model_config, nested_in: field)
|
|
96
|
+
contents.join.html_safe
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def object_infos
|
|
101
|
+
model_config = RailsAdminNext.config(object)
|
|
102
|
+
model_label = model_config.label
|
|
103
|
+
object_label =
|
|
104
|
+
if object.new_record?
|
|
105
|
+
I18n.t("admin.form.new_model", name: model_label)
|
|
106
|
+
else
|
|
107
|
+
object.send(model_config.object_label_method).presence || "#{model_config.label} ##{object.id}"
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
%(<span hidden class="object-infos" data-model-label="#{CGI.escapeHTML(model_label.to_s)}" data-object-label="#{CGI.escapeHTML(object_label.to_s)}"></span>).html_safe
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def dom_id(field)
|
|
114
|
+
(@dom_id ||= {})[field.name] ||=
|
|
115
|
+
[
|
|
116
|
+
@object_name.to_s.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, ""),
|
|
117
|
+
options[:index],
|
|
118
|
+
field.method_name
|
|
119
|
+
].reject(&:blank?).join("_")
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def dom_name(field)
|
|
123
|
+
(@dom_name ||= {})[field.name] ||= %(#{@object_name}#{options[:index] && "[#{options[:index]}]"}[#{field.method_name}]#{"[]" if field.is_a?(Config::Fields::Association) && field.multiple?})
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def hidden_field(method, options = {})
|
|
127
|
+
if method == :id && object.id.is_a?(Array)
|
|
128
|
+
super(method, {value: RailsAdminNext.config.composite_keys_serializer.serialize(object.id)})
|
|
129
|
+
else
|
|
130
|
+
super
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
protected
|
|
135
|
+
|
|
136
|
+
def generator_action(action, nested)
|
|
137
|
+
if nested
|
|
138
|
+
action = :nested
|
|
139
|
+
elsif @template.request.format == "text/javascript"
|
|
140
|
+
action = :modal
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
action
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def visible_groups(model_config, action)
|
|
147
|
+
model_config.send(action).with(
|
|
148
|
+
form: self,
|
|
149
|
+
object: @object,
|
|
150
|
+
view: @template,
|
|
151
|
+
controller: @template.controller
|
|
152
|
+
).visible_groups
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def without_field_error_proc_added_div
|
|
156
|
+
default_field_error_proc = ::ActionView::Base.field_error_proc
|
|
157
|
+
begin
|
|
158
|
+
::ActionView::Base.field_error_proc = proc { |html_tag, _instance| html_tag }
|
|
159
|
+
yield
|
|
160
|
+
ensure
|
|
161
|
+
::ActionView::Base.field_error_proc = default_field_error_proc
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
private
|
|
166
|
+
|
|
167
|
+
def nested_field_association?(field, nested_in)
|
|
168
|
+
field.inverse_of.presence && nested_in.presence && field.inverse_of == nested_in.name &&
|
|
169
|
+
(@template.instance_variable_get(:@model_config).abstract_model == field.abstract_model ||
|
|
170
|
+
field.name == nested_in.inverse_of)
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RailsAdminNext
|
|
4
|
+
module MainHelper
|
|
5
|
+
def rails_admin_form_for(*args, &)
|
|
6
|
+
options = args.extract_options!.reverse_merge(builder: RailsAdminNext::FormBuilder)
|
|
7
|
+
(options[:html] ||= {})[:novalidate] ||= !RailsAdminNext::Config.browser_validations
|
|
8
|
+
html_data = (options[:html][:data] ||= {})
|
|
9
|
+
html_data[:controller] = [html_data[:controller], "feedback"].compact.join(" ")
|
|
10
|
+
html_data[:action] = [html_data[:action], "turbo:submit-start->feedback#start turbo:submit-end->feedback#end"].compact.join(" ")
|
|
11
|
+
|
|
12
|
+
form_for(*(args << options), &)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def get_indicator(percent)
|
|
16
|
+
return "" if percent < 0 # none
|
|
17
|
+
return "info" if percent < 34 # < 1/100 of max
|
|
18
|
+
return "success" if percent < 67 # < 1/10 of max
|
|
19
|
+
return "warning" if percent < 84 # < 1/3 of max
|
|
20
|
+
|
|
21
|
+
"danger" # > 1/3 of max
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def filterable_fields
|
|
25
|
+
@filterable_fields ||= @model_config.list.fields.select(&:filterable?)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def ordered_filters
|
|
29
|
+
return @ordered_filters if @ordered_filters.present?
|
|
30
|
+
|
|
31
|
+
@index = 0
|
|
32
|
+
@ordered_filters = (params[:f].try(:permit!).try(:to_h) || @model_config.list.filters).each_with_object({}) do |filter, memo|
|
|
33
|
+
field_name = filter.is_a?(Array) ? filter.first : filter
|
|
34
|
+
(filter.is_a?(Array) ? filter.last : {(@index += 1) => {"v" => ""}}).each do |index, filter_hash|
|
|
35
|
+
if filter_hash["disabled"].blank?
|
|
36
|
+
memo[index] = {field_name => filter_hash}
|
|
37
|
+
else
|
|
38
|
+
params[:f].delete(field_name)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end.to_a.sort_by(&:first)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def ordered_filter_options
|
|
45
|
+
if ordered_filters
|
|
46
|
+
@ordered_filter_options ||= ordered_filters.map do |duplet|
|
|
47
|
+
filter_for_field = duplet[1]
|
|
48
|
+
filter_name = filter_for_field.keys.first
|
|
49
|
+
filter_hash = filter_for_field.values.first
|
|
50
|
+
unless (field = filterable_fields.find { |f| f.name == filter_name.to_sym }&.with({view: self}))
|
|
51
|
+
raise "#{filter_name} is not currently filterable; filterable fields are #{filterable_fields.map(&:name).join(", ")}"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
field.filter_options.merge(
|
|
55
|
+
index: duplet[0],
|
|
56
|
+
operator: filter_hash["o"] || field.default_filter_operator,
|
|
57
|
+
value: filter_hash["v"]
|
|
58
|
+
)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<%# locals: () -%>
|
|
2
|
+
<meta content="IE=edge" http-equiv="X-UA-Compatible">
|
|
3
|
+
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
|
4
|
+
<meta content="width=device-width, initial-scale=1" name="viewport; charset=utf-8">
|
|
5
|
+
<meta content="NONE,NOARCHIVE" name="robots">
|
|
6
|
+
<meta content="false" name="turbo-prefetch">
|
|
7
|
+
<%= csrf_meta_tag %>
|
|
8
|
+
<%# The scheme every light-dark() token pair in the engine CSS resolves against. Placed before the
|
|
9
|
+
stylesheet link so the scheme is known before first paint; a meta (not an inline style) keeps
|
|
10
|
+
CSP out of the picture and lets :light/:dark pins override the OS preference. %>
|
|
11
|
+
<meta content="<%= {auto: 'light dark', light: 'light', dark: 'dark'}.fetch(RailsAdminNext.config.color_scheme) %>" name="color-scheme">
|
|
12
|
+
<%# The entry stylesheet pulls tokens/framework/skin via @import, which the browser only discovers
|
|
13
|
+
after fetching and parsing the entry file. Preload the three targets so all four fetch in parallel. %>
|
|
14
|
+
<link rel="preload" as="style" href="<%= asset_path('rails_admin/tokens.css') %>">
|
|
15
|
+
<link rel="preload" as="style" href="<%= asset_path('rails_admin/framework.css') %>">
|
|
16
|
+
<link rel="preload" as="style" href="<%= asset_path('rails_admin/skin.css') %>">
|
|
17
|
+
<%# Date/time fields use native HTML5 inputs; the picker UI is the browser's. Icons are inline SVG (no icon font). %>
|
|
18
|
+
<%= stylesheet_link_tag "rails_admin", media: :all, data: {'turbo-track': 'reload'} %>
|
|
19
|
+
<%# Rich-text (ActionText) fields render a Trix editor; load its stylesheet so the toolbar is styled.
|
|
20
|
+
trix.css ships with the action_text-trix gem and is on the Propshaft load path when ActionText is used.
|
|
21
|
+
NOTE: trix.css is light-only for now — dark styling for the Trix toolbar/editor is deferred. %>
|
|
22
|
+
<% if defined?(ActionText::Engine) %>
|
|
23
|
+
<%= stylesheet_link_tag "trix", media: :all, data: {'turbo-track': 'reload'} %>
|
|
24
|
+
<% end %>
|
|
25
|
+
<%# Zero-build delivery: browser-native ESM pinned by the engine-owned importmap, served by Propshaft.
|
|
26
|
+
The importmap-rails helpers thread the request CSP nonce onto every inline <script> they emit. %>
|
|
27
|
+
<%= javascript_inline_importmap_tag(RailsAdminNext::Engine.importmap.to_json(resolver: self)) %>
|
|
28
|
+
<%= javascript_importmap_module_preload_tags(RailsAdminNext::Engine.importmap) %>
|
|
29
|
+
<%= javascript_import_module_tag 'rails_admin' %>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<%# locals: () -%>
|
|
2
|
+
<div class="container-fluid" data-controller="collapse">
|
|
3
|
+
<a class="navbar-brand" href="<%= dashboard_path %>">
|
|
4
|
+
Rails
|
|
5
|
+
<small>
|
|
6
|
+
AdminNext
|
|
7
|
+
</small>
|
|
8
|
+
</a>
|
|
9
|
+
<button class="navbar-toggler" type="button" data-collapse-target="trigger" data-action="collapse#toggle" aria-expanded="false" aria-controls="secondary-navigation">
|
|
10
|
+
<span class="sr-only">
|
|
11
|
+
<%= t('admin.toggle_navigation') %>
|
|
12
|
+
</span>
|
|
13
|
+
<span class="navbar-toggler-icon"></span>
|
|
14
|
+
</button>
|
|
15
|
+
<div class="collapse navbar-collapse" id="secondary-navigation" data-collapse-target="region">
|
|
16
|
+
<%= render partial: 'layouts/rails_admin_next/secondary_navigation' %>
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<%# locals: () -%>
|
|
2
|
+
<ul class="navbar-nav ms-auto root_links">
|
|
3
|
+
<% actions(:root).select(&:show_in_navigation).each do |action| %>
|
|
4
|
+
<li class="nav-item <%= action.action_name %>_root_link">
|
|
5
|
+
<%= link_to wording_for(:menu, action), { action: action.action_name, controller: 'rails_admin_next/main' }, {class: ['nav-link']}.merge(action.turbo? ? {} : {data: {turbo: 'false'}}) %>
|
|
6
|
+
</li>
|
|
7
|
+
<% end %>
|
|
8
|
+
<% if main_app_root_path = (main_app.root_path rescue false) %>
|
|
9
|
+
<li class="nav-item">
|
|
10
|
+
<%= link_to t('admin.home.name'), main_app_root_path, class: 'nav-link' %>
|
|
11
|
+
</li>
|
|
12
|
+
<% end %>
|
|
13
|
+
<% if _current_user %>
|
|
14
|
+
<% if user_link = edit_user_link %>
|
|
15
|
+
<li class="nav-item edit_user_root_link">
|
|
16
|
+
<%= user_link %>
|
|
17
|
+
</li>
|
|
18
|
+
<% end %>
|
|
19
|
+
<% if logout_path.present? %>
|
|
20
|
+
<li class="nav-item">
|
|
21
|
+
<%= link_to logout_path, class: 'nav-link', data: {turbo_method: logout_method} do %>
|
|
22
|
+
<span class="badge bg-danger"><%= t('admin.misc.log_out') %></span>
|
|
23
|
+
<% end %>
|
|
24
|
+
</li>
|
|
25
|
+
<% end %>
|
|
26
|
+
<% end %>
|
|
27
|
+
</ul>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="<%= I18n.locale %>">
|
|
3
|
+
<head>
|
|
4
|
+
<%= render "layouts/rails_admin_next/head" %>
|
|
5
|
+
</head>
|
|
6
|
+
<body class="rails_admin">
|
|
7
|
+
<div data-i18n-options="<%= I18n.t("admin.js").to_json %>" id="admin-js"></div>
|
|
8
|
+
<div class="badge bg-warning" id="loading" style="display:none; position:fixed; right:20px; bottom:20px; z-index:100000">
|
|
9
|
+
<%= t('admin.loading') %>
|
|
10
|
+
</div>
|
|
11
|
+
<nav class="navbar navbar-expand-md fixed-top <%= RailsAdminNext::Config.navbar_css_classes.join(' ') %>">
|
|
12
|
+
<%= render "layouts/rails_admin_next/navigation" %>
|
|
13
|
+
</nav>
|
|
14
|
+
<div class="container-fluid">
|
|
15
|
+
<div class="row">
|
|
16
|
+
<div class="col-sm-3 col-md-2 flex-wrap p-0">
|
|
17
|
+
<%= render "layouts/rails_admin_next/sidebar_navigation" %>
|
|
18
|
+
</div>
|
|
19
|
+
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2">
|
|
20
|
+
<div class="container-fluid">
|
|
21
|
+
<%= render template: 'layouts/rails_admin_next/content' %>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
<%# Native dialog: the platform owns the top layer, focus trap, Escape and
|
|
27
|
+
(via closedby="any") backdrop light-dismissal. The title is seeded with
|
|
28
|
+
the loading string and the body flagged busy so a just-opened dialog
|
|
29
|
+
has an accessible name and state before the async form arrives. %>
|
|
30
|
+
<dialog id="modal" closedby="any" aria-labelledby="modal-title" data-controller="modal">
|
|
31
|
+
<div class="modal-header">
|
|
32
|
+
<h3 class="modal-header-title" id="modal-title" data-modal-target="title"><%= t('admin.loading') %></h3>
|
|
33
|
+
<button type="button" class="btn-close" data-action="modal#close" aria-label="<%= t('admin.form.cancel') %>"></button>
|
|
34
|
+
</div>
|
|
35
|
+
<div class="modal-body" aria-busy="true" data-modal-target="body"></div>
|
|
36
|
+
<div class="modal-footer">
|
|
37
|
+
<a href="#" class="btn cancel-action" data-modal-target="cancelButton" data-action="modal#close"></a>
|
|
38
|
+
<a href="#" class="btn btn-primary save-action" data-modal-target="saveButton" data-action="modal#save"></a>
|
|
39
|
+
</div>
|
|
40
|
+
</dialog>
|
|
41
|
+
</body>
|
|
42
|
+
</html>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<title>
|
|
2
|
+
<%= "#{@abstract_model.try(:pretty_name) || @page_name} | Rails AdminNext" %>
|
|
3
|
+
</title>
|
|
4
|
+
<header class="py-2 m-2 border-bottom" data-model="<%= @abstract_model.to_param %>">
|
|
5
|
+
<h1>
|
|
6
|
+
<%= @page_name %>
|
|
7
|
+
</h1>
|
|
8
|
+
</header>
|
|
9
|
+
<% flash && flash.each do |key, value| %>
|
|
10
|
+
<div class="<%= flash_alert_class(key) %> alert alert-dismissible" data-controller="alert">
|
|
11
|
+
<%= value %>
|
|
12
|
+
<button type="button" class="btn-close" data-action="alert#dismiss" aria-label="Close"></button>
|
|
13
|
+
</div>
|
|
14
|
+
<% end %>
|
|
15
|
+
<nav aria-label="breadcrumb">
|
|
16
|
+
<%= breadcrumb %>
|
|
17
|
+
</nav>
|
|
18
|
+
<ul class="nav nav-tabs mb-3">
|
|
19
|
+
<%= menu_for((@abstract_model ? (@object.try(:persisted?) ? :member : :collection) : :root), @abstract_model, @object) %>
|
|
20
|
+
<%= content_for :contextual_tabs %>
|
|
21
|
+
</ul>
|
|
22
|
+
<%= yield %>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<% flash && flash.each do |key, value| %>
|
|
2
|
+
<div class="<%= flash_alert_class(key) %> alert alert-dismissible" data-controller="alert">
|
|
3
|
+
<%= value %>
|
|
4
|
+
<button type="button" class="btn-close" data-action="alert#dismiss" aria-label="Close"></button>
|
|
5
|
+
</div>
|
|
6
|
+
<% end %>
|
|
7
|
+
<%= yield %>
|