swop 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/LICENSE +201 -0
- data/README.md +15 -0
- data/Rakefile +4 -0
- data/app/assets/javascripts/rails_admin/application.js.erb +32 -0
- data/app/assets/javascripts/rails_admin/custom/filtering-select.js +304 -0
- data/app/assets/javascripts/rails_admin/custom/sidescroll.js +20 -0
- data/app/assets/javascripts/rails_admin/custom/ui.js +11 -0
- data/app/assets/stylesheets/rails_admin/application.scss.erb +52 -0
- data/app/assets/stylesheets/rails_admin/custom/base/base.scss +5 -0
- data/app/assets/stylesheets/rails_admin/custom/base/bootstrap-variables.scss +196 -0
- data/app/assets/stylesheets/rails_admin/custom/base/mixins.scss +88 -0
- data/app/assets/stylesheets/rails_admin/custom/base/typography.scss +16 -0
- data/app/assets/stylesheets/rails_admin/custom/base/variables.scss +86 -0
- data/app/assets/stylesheets/rails_admin/custom/components/breadcrumbs.scss +18 -0
- data/app/assets/stylesheets/rails_admin/custom/components/buttons.scss +55 -0
- data/app/assets/stylesheets/rails_admin/custom/components/dropdowns.scss +40 -0
- data/app/assets/stylesheets/rails_admin/custom/components/filtering-select.scss +43 -0
- data/app/assets/stylesheets/rails_admin/custom/components/forms.scss +25 -0
- data/app/assets/stylesheets/rails_admin/custom/components/images.scss +21 -0
- data/app/assets/stylesheets/rails_admin/custom/components/navbar.scss +18 -0
- data/app/assets/stylesheets/rails_admin/custom/components/navs.scss +28 -0
- data/app/assets/stylesheets/rails_admin/custom/components/offcanvas.scss +18 -0
- data/app/assets/stylesheets/rails_admin/custom/components/progress.scss +11 -0
- data/app/assets/stylesheets/rails_admin/custom/components/sidebar.scss +115 -0
- data/app/assets/stylesheets/rails_admin/custom/components/table.scss +59 -0
- data/app/assets/stylesheets/rails_admin/custom/components/tiles.scss +20 -0
- data/app/assets/stylesheets/rails_admin/custom/layouts/body.scss +10 -0
- data/app/assets/stylesheets/rails_admin/custom/layouts/containers.scss +31 -0
- data/app/helpers/rails_admin/application_helper.rb +270 -0
- data/app/views/layouts/rails_admin/_sidebar_navigation.html.erb +1 -0
- data/app/views/layouts/rails_admin/application.html.erb +24 -0
- data/app/views/layouts/rails_admin/content.html.erb +57 -0
- data/app/views/layouts/rails_admin/custom/_nav.html.erb +10 -0
- data/app/views/rails_admin/main/_submit_buttons.html.erb +25 -0
- data/app/views/rails_admin/main/dashboard.html.erb +70 -0
- data/app/views/rails_admin/main/delete.html.erb +19 -0
- data/app/views/rails_admin/main/edit.html.erb +5 -0
- data/app/views/rails_admin/main/export.html.erb +128 -0
- data/app/views/rails_admin/main/index.html.erb +170 -0
- data/app/views/rails_admin/main/new.html.erb +5 -0
- data/app/views/rails_admin/main/show.html.erb +28 -0
- data/lib/swop/engine.rb +7 -0
- data/lib/swop/version.rb +5 -0
- data/lib/swop.rb +8 -0
- data/sig/swop.rbs +4 -0
- metadata +140 -0
@@ -0,0 +1,270 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsAdmin
|
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
|
+
RailsAdmin::Config::Actions.find(key, controller: controller, abstract_model: abstract_model, object: object)
|
22
|
+
end
|
23
|
+
|
24
|
+
def actions(scope = :all, abstract_model = nil, object = nil)
|
25
|
+
RailsAdmin::Config::Actions.all(scope, controller: controller, abstract_model: abstract_model, object: object)
|
26
|
+
end
|
27
|
+
|
28
|
+
def edit_user_link
|
29
|
+
return nil unless _current_user.try(:email).present?
|
30
|
+
return nil unless (abstract_model = RailsAdmin.config(_current_user.class).abstract_model)
|
31
|
+
|
32
|
+
edit_action = action(:edit, abstract_model, _current_user)
|
33
|
+
authorized = edit_action.try(:authorized?)
|
34
|
+
content = edit_user_link_label
|
35
|
+
|
36
|
+
if authorized
|
37
|
+
edit_url = rails_admin.url_for(
|
38
|
+
action: edit_action.action_name,
|
39
|
+
model_name: abstract_model.to_param,
|
40
|
+
controller: 'rails_admin/main',
|
41
|
+
id: _current_user.id,
|
42
|
+
)
|
43
|
+
|
44
|
+
link_to content, edit_url, class: 'nav-link'
|
45
|
+
else
|
46
|
+
content_tag :span, content, class: 'nav-link'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def logout_path
|
51
|
+
if defined?(Devise)
|
52
|
+
scope = Devise::Mapping.find_scope!(_current_user)
|
53
|
+
begin
|
54
|
+
main_app.send("destroy_#{scope}_session_path")
|
55
|
+
rescue StandardError
|
56
|
+
false
|
57
|
+
end
|
58
|
+
elsif main_app.respond_to?(:logout_path)
|
59
|
+
main_app.logout_path
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def logout_method
|
64
|
+
return [Devise.sign_out_via].flatten.first if defined?(Devise)
|
65
|
+
|
66
|
+
:delete
|
67
|
+
end
|
68
|
+
|
69
|
+
def wording_for(label, action = @action, abstract_model = @abstract_model, object = @object)
|
70
|
+
model_config = abstract_model.try(:config)
|
71
|
+
object = nil unless abstract_model && object.is_a?(abstract_model.model)
|
72
|
+
action = RailsAdmin::Config::Actions.find(action.to_sym) if action.is_a?(Symbol) || action.is_a?(String)
|
73
|
+
|
74
|
+
I18n.t(
|
75
|
+
"admin.actions.#{action.i18n_key}.#{label}",
|
76
|
+
model_label: model_config&.label,
|
77
|
+
model_label_plural: model_config&.label_plural,
|
78
|
+
object_label: model_config && object.try(model_config.object_label_method),
|
79
|
+
)
|
80
|
+
end
|
81
|
+
|
82
|
+
def main_navigation
|
83
|
+
nodes_stack = RailsAdmin::Config.visible_models(controller: controller)
|
84
|
+
node_model_names = nodes_stack.collect { |c| c.abstract_model.model_name }
|
85
|
+
parent_groups = nodes_stack.group_by { |n| n.parent&.to_s }
|
86
|
+
|
87
|
+
nodes_stack.group_by(&:navigation_label).collect do |navigation_label, nodes|
|
88
|
+
nodes = nodes.select { |n| n.parent.nil? || !n.parent.to_s.in?(node_model_names) }
|
89
|
+
li_stack = navigation parent_groups, nodes
|
90
|
+
|
91
|
+
label = navigation_label || t('admin.misc.navigation')
|
92
|
+
|
93
|
+
collapsible_stack(label, 'main', li_stack)
|
94
|
+
end.join.html_safe
|
95
|
+
end
|
96
|
+
|
97
|
+
def root_navigation
|
98
|
+
actions(:root).select(&:show_in_sidebar).group_by(&:sidebar_label).collect do |label, nodes|
|
99
|
+
li_stack = nodes.map do |node|
|
100
|
+
url = rails_admin.url_for(action: node.action_name, controller: 'rails_admin/main')
|
101
|
+
nav_icon = node.link_icon ? %(<i class="#{node.link_icon}"></i>).html_safe : ''
|
102
|
+
content_tag :li do
|
103
|
+
link_to nav_icon + " " + wording_for(:menu, node), url, class: "nav-link"
|
104
|
+
end
|
105
|
+
end.join.html_safe
|
106
|
+
label ||= t('admin.misc.root_navigation')
|
107
|
+
|
108
|
+
collapsible_stack(label, 'action', li_stack)
|
109
|
+
end.join.html_safe
|
110
|
+
end
|
111
|
+
|
112
|
+
def static_navigation
|
113
|
+
li_stack = RailsAdmin::Config.navigation_static_links.collect do |title, url|
|
114
|
+
content_tag(:li, link_to(title.to_s, url, target: '_blank', rel: 'noopener noreferrer', class: 'nav-link'))
|
115
|
+
end.join.html_safe
|
116
|
+
|
117
|
+
label = RailsAdmin::Config.navigation_static_label || t('admin.misc.navigation_static_label')
|
118
|
+
collapsible_stack(label, 'static', li_stack) || ''
|
119
|
+
end
|
120
|
+
|
121
|
+
def navigation(parent_groups, nodes, level = 0)
|
122
|
+
nodes.collect do |node|
|
123
|
+
abstract_model = node.abstract_model
|
124
|
+
model_param = abstract_model.to_param
|
125
|
+
url = rails_admin.url_for(action: :index, controller: 'rails_admin/main', model_name: model_param)
|
126
|
+
nav_icon = node.navigation_icon ? %(<i class="#{node.navigation_icon}"></i>).html_safe : ''
|
127
|
+
css_classes = ['nav-link']
|
128
|
+
css_classes.push("nav-level-#{level}") if level > 0
|
129
|
+
css_classes.push('active') if @action && current_action?(@action, model_param)
|
130
|
+
li = content_tag :li, data: {model: model_param} do
|
131
|
+
link_to nav_icon + " " + node.label_plural, url, class: css_classes.join(' ')
|
132
|
+
end
|
133
|
+
child_nodes = parent_groups[abstract_model.model_name]
|
134
|
+
child_nodes ? li + navigation(parent_groups, child_nodes, level + 1) : li
|
135
|
+
end.join.html_safe
|
136
|
+
end
|
137
|
+
|
138
|
+
def breadcrumb(action = @action, _acc = [])
|
139
|
+
begin
|
140
|
+
(parent_actions ||= []) << action
|
141
|
+
end while action.breadcrumb_parent && (action = action(*action.breadcrumb_parent)) # rubocop:disable Lint/Loop
|
142
|
+
|
143
|
+
content_tag(:ol, class: 'breadcrumb') do
|
144
|
+
parent_actions.collect do |a|
|
145
|
+
am = a.send(:eval, 'bindings[:abstract_model]')
|
146
|
+
o = a.send(:eval, 'bindings[:object]')
|
147
|
+
content_tag(:li, class: ['breadcrumb-item', current_action?(a, am, o) && 'active']) do
|
148
|
+
if current_action?(a, am, o)
|
149
|
+
wording_for(:breadcrumb, a, am, o)
|
150
|
+
elsif a.http_methods.include?(:get)
|
151
|
+
link_to rails_admin.url_for(action: a.action_name, controller: 'rails_admin/main', model_name: am.try(:to_param), id: (o.try(:persisted?) && o.try(:id) || nil)) do
|
152
|
+
wording_for(:breadcrumb, a, am, o)
|
153
|
+
end
|
154
|
+
else
|
155
|
+
content_tag(:span, wording_for(:breadcrumb, a, am, o))
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end.reverse.join.html_safe
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# parent => :root, :collection, :member
|
163
|
+
# perf matters here (no action view trickery)
|
164
|
+
def menu_for(parent, abstract_model = nil, object = nil, only_icon = false)
|
165
|
+
actions = actions(parent, abstract_model, object).select { |a| a.http_methods.include?(:get) && a.show_in_menu }
|
166
|
+
|
167
|
+
# Filter out list action
|
168
|
+
actions.reject! { |action| %i[index].include?(action.key) }
|
169
|
+
|
170
|
+
# Sort actions
|
171
|
+
actions.sort_by! { |action| action.key.to_s }
|
172
|
+
|
173
|
+
actions.collect do |action|
|
174
|
+
wording = wording_for(:menu, action)
|
175
|
+
link_class = ["#{action.key}_#{parent}_link"].concat(action.enabled? ? [] : ['disabled'])
|
176
|
+
label = content_tag(:i, '', {class: "fa-sm fa-fw #{action.link_icon}"}) + ' ' + content_tag(:span, wording, (only_icon ? {style: 'display:none'} : {}))
|
177
|
+
if action.enabled? || !only_icon
|
178
|
+
href =
|
179
|
+
if action.enabled?
|
180
|
+
rails_admin.url_for(action: action.action_name, controller: 'rails_admin/main', model_name: abstract_model.try(:to_param), id: (object.try(:persisted?) && object.try(:id) || nil))
|
181
|
+
else
|
182
|
+
'javascript:void(0)'
|
183
|
+
end
|
184
|
+
button_class = action.key == :new ? 'btn btn-primary' : 'btn btn-white'
|
185
|
+
content_tag(:div, class: "col-md-auto") do
|
186
|
+
content_tag(:a, label, {href: href, target: action.link_target, class: [link_class, button_class, !action.enabled? && 'disabled'].compact}.merge(action.turbo? ? {} : {data: {turbo: 'false'}}))
|
187
|
+
end
|
188
|
+
else
|
189
|
+
content_tag(:span, label)
|
190
|
+
end
|
191
|
+
end.join(' ').html_safe
|
192
|
+
end
|
193
|
+
|
194
|
+
def bulk_menu(abstract_model = @abstract_model)
|
195
|
+
actions = actions(:bulkable, abstract_model)
|
196
|
+
return '' if actions.empty?
|
197
|
+
content_tag(:div, class: "dropdown") do
|
198
|
+
content_tag(:button, class: 'btn btn-white dropdown-toggle', data: {'bs-toggle': 'dropdown'}) { t('admin.misc.bulk_menu_title').html_safe + ' ' + '<b class="caret"></b>'.html_safe } +
|
199
|
+
content_tag(:ul, class: 'dropdown-menu', style: 'left:auto; right:0;') do
|
200
|
+
actions.collect do |action|
|
201
|
+
content_tag :li do
|
202
|
+
link_to wording_for(:bulk_link, action, abstract_model), '#', class: 'dropdown-item bulk-link', data: {action: action.action_name}
|
203
|
+
end
|
204
|
+
end.join.html_safe
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def flash_alert_class(flash_key)
|
210
|
+
case flash_key.to_s
|
211
|
+
when 'error' then 'alert-danger'
|
212
|
+
when 'alert' then 'alert-warning'
|
213
|
+
when 'notice' then 'alert-info'
|
214
|
+
else "alert-#{flash_key}"
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def handle_asset_dependency_error
|
219
|
+
yield
|
220
|
+
rescue LoadError => e
|
221
|
+
if /sassc/.match?(e.message)
|
222
|
+
e = e.exception <<~MSG
|
223
|
+
#{e.message}
|
224
|
+
RailsAdmin requires the gem sassc-rails, make sure to put `gem 'sassc-rails'` to Gemfile.
|
225
|
+
MSG
|
226
|
+
end
|
227
|
+
raise e
|
228
|
+
end
|
229
|
+
|
230
|
+
# Workaround for https://github.com/rails/rails/issues/31325
|
231
|
+
def image_tag(source, options = {})
|
232
|
+
if %w[ActiveStorage::Variant ActiveStorage::VariantWithRecord ActiveStorage::Preview].include? source.class.to_s
|
233
|
+
super main_app.route_for(ActiveStorage.resolve_model_to_route, source), options
|
234
|
+
else
|
235
|
+
super
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
private
|
240
|
+
|
241
|
+
def edit_user_link_label
|
242
|
+
[
|
243
|
+
RailsAdmin::Config.show_gravatar &&
|
244
|
+
image_tag(gravatar_url(_current_user.email), alt: ''),
|
245
|
+
|
246
|
+
content_tag(:span, _current_user.email),
|
247
|
+
].filter(&:present?).join.html_safe
|
248
|
+
end
|
249
|
+
|
250
|
+
def gravatar_url(email)
|
251
|
+
"https://secure.gravatar.com/avatar/#{Digest::MD5.hexdigest email}?s=30"
|
252
|
+
end
|
253
|
+
|
254
|
+
def collapsible_stack(label, class_prefix, li_stack)
|
255
|
+
return nil unless li_stack.present?
|
256
|
+
|
257
|
+
collapse_classname = "#{class_prefix}-#{Digest::MD5.hexdigest(label)[0..7]}"
|
258
|
+
content_tag(:li, class: 'mb-1') do
|
259
|
+
content_tag(:button, 'aria-expanded': true, class: 'btn btn-toggle align-items-center rounded', data: {bs_toggle: "collapse", bs_target: ".sidebar .#{collapse_classname}"}) do
|
260
|
+
content_tag(:i, '', class: 'fas fa-chevron-down') + html_escape(' ' + label)
|
261
|
+
end +
|
262
|
+
content_tag(:div, class: "collapse show #{collapse_classname}") do
|
263
|
+
content_tag(:ul, class: 'btn-toggle-nav list-unstyled fw-normal pb-1') do
|
264
|
+
li_stack
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= render partial: "layouts/rails_admin/custom/nav" %>
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="<%= I18n.locale %>">
|
3
|
+
<head>
|
4
|
+
<%= render "layouts/rails_admin/head" %>
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
6
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
7
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
8
|
+
<link href="https://fonts.googleapis.com/css2?family=Mulish:ital,wght@0,200..1000;1,200..1000&display=swap" rel="stylesheet">
|
9
|
+
<title>
|
10
|
+
<%= "#{@abstract_model.try(:pretty_name) || @page_name} | #{[_get_plugin_name[0] || 'Rails', _get_plugin_name[1] || 'Admin'].join(' ')}" %>
|
11
|
+
</title>
|
12
|
+
</head>
|
13
|
+
<body class="rails_admin">
|
14
|
+
<div data-i18n-options="<%= I18n.t("admin.js").to_json %>" id="admin-js"></div>
|
15
|
+
<!-- To-DO: Review this -->
|
16
|
+
<div class="badge bg-warning" id="loading" style="display:none; position:fixed; right:20px; bottom:20px; z-index:100000">
|
17
|
+
<%= t('admin.loading') %>
|
18
|
+
</div>
|
19
|
+
<%= render "layouts/rails_admin/sidebar_navigation" %>
|
20
|
+
<div class="container-fluid position-relative">
|
21
|
+
<%= render template: 'layouts/rails_admin/content' %>
|
22
|
+
</div>
|
23
|
+
</body>
|
24
|
+
</html>
|
@@ -0,0 +1,57 @@
|
|
1
|
+
<header class="header mb-4 mb-md-5 mb-lg-6">
|
2
|
+
<div class="row align-items-center gx-4 gy-0 gx-md-8">
|
3
|
+
<div class="col text-truncate">
|
4
|
+
<nav aria-label="breadcrumb">
|
5
|
+
<%= breadcrumb %>
|
6
|
+
</nav>
|
7
|
+
<h1 class="text-truncate mb-0"><%= @page_name %></h1>
|
8
|
+
</div>
|
9
|
+
<div class="col-auto">
|
10
|
+
<div class="dropdown dropdown-admin">
|
11
|
+
<button class="btn text-start p-0 d-flex align-items-center" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
12
|
+
<div class="img-wrapper-avatar flex-shrink-0 shadow">
|
13
|
+
<%= image_tag(gravatar_url(_current_user.email), alt: "", title: "") %>
|
14
|
+
</div>
|
15
|
+
<!-- if md breakpoint -->
|
16
|
+
<div class="text-truncate ms-md-4 d-none d-md-block">
|
17
|
+
<div class="text-dark fw-bold lh-sm text-truncate"><%= _current_user.name %></div>
|
18
|
+
</div>
|
19
|
+
</button>
|
20
|
+
<!-- TO-DO: Style dropdowns -->
|
21
|
+
<% edit_url = rails_admin.url_for(
|
22
|
+
action: :edit,
|
23
|
+
model_name: RailsAdmin.config(_current_user.class).abstract_model.to_param,
|
24
|
+
controller: 'rails_admin/main',
|
25
|
+
id: _current_user.id
|
26
|
+
) %>
|
27
|
+
<ul class="dropdown-menu dropdown-menu-end">
|
28
|
+
<li><%= link_to "#{icon "fa-solid", "user", class: "fa-sm fa-fw me-2"}Your account".html_safe, edit_url, class: "dropdown-item" %></li>
|
29
|
+
<li><%= link_to "#{icon "fa-solid", "right-from-bracket", class: "fa-sm fa-fw me-2"}Log out".html_safe, logout_path, method: logout_method, class: "dropdown-item", data: {turbo: 'false'} %></li>
|
30
|
+
</ul>
|
31
|
+
</div>
|
32
|
+
</div>
|
33
|
+
<!-- unless lg breakpoint -->
|
34
|
+
<div class="col-auto d-lg-none">
|
35
|
+
<button class="navbar-toggler" type="button" data-bs-toggle="offcanvas" data-bs-target="#offcanvasNavbar" aria-controls="offcanvasNavbar" aria-label="Toggle navigation">
|
36
|
+
<span class="navbar-toggler-icon"></span>
|
37
|
+
<span class="navbar-toggler-icon"></span>
|
38
|
+
<span class="navbar-toggler-icon"></span>
|
39
|
+
</button>
|
40
|
+
<div class="offcanvas offcanvas-start" tabindex="-1" id="offcanvasNavbar" aria-labelledby="offcanvasNavbarLabel">
|
41
|
+
<div class="offcanvas-body">
|
42
|
+
<%= render partial: "layouts/rails_admin/custom/nav" %>
|
43
|
+
</div>
|
44
|
+
</div>
|
45
|
+
</div>
|
46
|
+
</div>
|
47
|
+
</header>
|
48
|
+
|
49
|
+
<!-- TO-DO: Review this -->
|
50
|
+
<% flash && flash.each do |key, value| %>
|
51
|
+
<div class="<%= flash_alert_class(key) %> alert alert-dismissible">
|
52
|
+
<%= value %>
|
53
|
+
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
54
|
+
</div>
|
55
|
+
<% end %>
|
56
|
+
|
57
|
+
<%= yield %>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<ul class="sidebar list-unstyled mb-0">
|
2
|
+
<% actions(:root).select(&:show_in_navigation).each do |action| %>
|
3
|
+
<li class="nav-item <%= action.action_name %>_root_link">
|
4
|
+
<%= link_to "<i class='fas fa-fw fa-sm fa-pie-chart'></i>#{wording_for(:menu, action)}".html_safe, { action: action.action_name, controller: "rails_admin/main" }, {class: ["nav-link #{"active" if current_page?("/")}"]}.merge(action.turbo? ? {} : {data: {turbo: "false"}}) %>
|
5
|
+
</li>
|
6
|
+
<% end %>
|
7
|
+
<%= main_navigation %>
|
8
|
+
<%= root_navigation %>
|
9
|
+
<%= static_navigation %>
|
10
|
+
</ul>
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<div class="form-actions row justify-content-end my-3">
|
2
|
+
<div class="col-sm-24 d-flex justify-content-end gap-3 flex-column flex-md-row">
|
3
|
+
<input name="return_to" type="<%= :hidden %>" value="<%= (params[:return_to].presence || request.referer) %>" />
|
4
|
+
<button class="btn btn-primary" data-disable-with="<%= t("admin.form.save") %>" name="_save" type="submit"<%= ' disabled' unless @action.enabled? %>>
|
5
|
+
<i class="fas fa-check"></i>
|
6
|
+
<%= t("admin.form.save") %>
|
7
|
+
</button>
|
8
|
+
<span class="extra_buttons d-flex flex-column flex-md-row gap-3">
|
9
|
+
<% if @action.enabled? && authorized?(:new, @abstract_model) %>
|
10
|
+
<button class="btn btn-white" data-disable-with="<%= t("admin.form.save_and_add_another") %>" name="_add_another" type="submit">
|
11
|
+
<%= t("admin.form.save_and_add_another") %>
|
12
|
+
</button>
|
13
|
+
<% end %>
|
14
|
+
<% if @action.enabled? && authorized?(:edit, @abstract_model) %>
|
15
|
+
<button class="btn btn-white" data-disable-with="<%= t("admin.form.save_and_edit") %>" name="_add_edit" type="submit"<%= ' disabled' unless @action.enabled? %>>
|
16
|
+
<%= t("admin.form.save_and_edit") %>
|
17
|
+
</button>
|
18
|
+
<% end %>
|
19
|
+
<button class="btn btn-light" data-disable-with="<%= t("admin.form.cancel") %>" formnovalidate="<%= true %>" name="_continue" type="submit">
|
20
|
+
<i class="fas fa-times"></i>
|
21
|
+
<%= t("admin.form.cancel") %>
|
22
|
+
</button>
|
23
|
+
</span>
|
24
|
+
</div>
|
25
|
+
</div>
|
@@ -0,0 +1,70 @@
|
|
1
|
+
<% if @abstract_models %>
|
2
|
+
<section class="bg-white p-4 p-md-5 p-lg-6 shadow rounded-3">
|
3
|
+
<div class="row align-items-baseline gx-3 gy-0 gx-md-4 gx-lg-5 gx-xl-6">
|
4
|
+
<div class="col-11 col-md-6">
|
5
|
+
<div class="small text-muted fw-semibold"><%= t "admin.table_headers.model_name" %></div>
|
6
|
+
</div>
|
7
|
+
<div class="col order-md-last">
|
8
|
+
<div class="small text-muted fw-semibold"><%= t "admin.table_headers.records" %></div>
|
9
|
+
</div>
|
10
|
+
<!-- if md breakpoint -->
|
11
|
+
<div class="col col-md-7 d-none d-md-block">
|
12
|
+
<div class="small text-muted fw-semibold"><%= t "admin.table_headers.last_created" %></div>
|
13
|
+
</div>
|
14
|
+
</div>
|
15
|
+
<% @abstract_models.each do |abstract_model| %>
|
16
|
+
<% if authorized? :index, abstract_model %>
|
17
|
+
<hr class="my-1">
|
18
|
+
<% index_path = index_path(model_name: abstract_model.to_param) %>
|
19
|
+
<% last_created = @most_recent_created[abstract_model.model.name] %>
|
20
|
+
<% active = last_created.try(:today?) %>
|
21
|
+
<div class="row align-items-baseline gx-3 gy-0 gx-md-4 gx-lg-5 gx-xl-6 small">
|
22
|
+
<div class="col-11 col-md-6">
|
23
|
+
<%= link_to abstract_model.config.label_plural, index_path, class: "fw-bold text-dark" %>
|
24
|
+
<% if last_created %>
|
25
|
+
<div class="small lh-sm d-md-none">
|
26
|
+
<%= t "admin.misc.time_ago", time: time_ago_in_words(last_created), default: "#{time_ago_in_words(last_created)} #{t("admin.misc.ago")}" if last_created %>
|
27
|
+
</div>
|
28
|
+
<% end %>
|
29
|
+
</div>
|
30
|
+
<div class="col order-md-last">
|
31
|
+
<% count = @count[abstract_model.model.name] %>
|
32
|
+
<% percent = count > 0 ? (@max <= 1 ? count : ((Math.log(count+1) * 100.0) / Math.log(@max+1)).to_i) : -1 %>
|
33
|
+
<div class="<%= active ? 'active progress-bar-striped ' : '' %>progress" style="margin-bottom:0px">
|
34
|
+
<div class="bg-<%= get_indicator(percent) %> progress-bar animate-width-to" data-animate-length="<%= ([1.0, percent].max.to_i * 20) %>" data-animate-width-to="<%= [2.0, percent].max.to_i %>%" style="width:2%">
|
35
|
+
<%= @count[abstract_model.model.name] %>
|
36
|
+
</div>
|
37
|
+
</div>
|
38
|
+
</div>
|
39
|
+
<div class="col-auto order-md-last">
|
40
|
+
<div class="dropdown">
|
41
|
+
<button class="btn btn-dropdown btn-sm btn-square" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
42
|
+
<i class="fas fa-ellipsis-vertical"></i>
|
43
|
+
</button>
|
44
|
+
<ul class="dropdown-menu">
|
45
|
+
<%= menu_for :collection, abstract_model, nil, false %>
|
46
|
+
</ul>
|
47
|
+
</div>
|
48
|
+
</div>
|
49
|
+
<!-- if md breakpoint -->
|
50
|
+
<div class="col col-md-7 d-none d-md-block">
|
51
|
+
<%= t "admin.misc.time_ago", time: time_ago_in_words(last_created), default: "#{time_ago_in_words(last_created)} #{t("admin.misc.ago")}" if last_created %>
|
52
|
+
</div>
|
53
|
+
</div>
|
54
|
+
<% end %>
|
55
|
+
<% end %>
|
56
|
+
</section>
|
57
|
+
<% end %>
|
58
|
+
|
59
|
+
|
60
|
+
<!-- TO-DO: Review this -->
|
61
|
+
<% if @history && authorized?(:history_index) %>
|
62
|
+
<div class="block" id="block-tables">
|
63
|
+
<div class="content">
|
64
|
+
<h2>
|
65
|
+
<%= t("admin.actions.history_index.menu") %>
|
66
|
+
</h2>
|
67
|
+
<%= render partial: 'rails_admin/main/dashboard_history' %>
|
68
|
+
</div>
|
69
|
+
</div>
|
70
|
+
<% end %>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
<section class="bg-white p-5 p-md-7 p-lg-9 mb-5 mb-md-6 shadow rounded-3">
|
2
|
+
<p class="fw-semibold mb-0"><%= t("admin.form.are_you_sure_you_want_to_delete_the_object", model_name: @abstract_model.pretty_name.downcase) %> <q><strong><%= @model_config.with(object: @object).object_label %></strong></q><%= t("admin.form.all_of_the_following_related_items_will_be_deleted") %></p>
|
3
|
+
<ul class="ps-0 mb-4 mb-md-5">
|
4
|
+
<%= render partial: "delete_notice", object: @object %>
|
5
|
+
</ul>
|
6
|
+
<%= form_for(@object, url: delete_path(model_name: @abstract_model.to_param, id: @object.id), html: {method: "delete"}) do %>
|
7
|
+
<input name="return_to" type="<%= :hidden %>" value="<%= (params[:return_to].presence || request.referer) %>" />
|
8
|
+
<div class="form-actions">
|
9
|
+
<button class="btn btn-danger" data-disable-with="<%= t("admin.form.confirmation") %>" type="submit">
|
10
|
+
<i class="fas fa-check"></i>
|
11
|
+
<%= t("admin.form.confirmation") %>
|
12
|
+
</button>
|
13
|
+
<button class="btn btn-white ms-3" data-disable-with="<%= t("admin.form.cancel") %>" name="_continue" type="submit">
|
14
|
+
<i class="fas fa-times"></i>
|
15
|
+
<%= t("admin.form.cancel") %>
|
16
|
+
</button>
|
17
|
+
</div>
|
18
|
+
<% end %>
|
19
|
+
</section>
|
@@ -0,0 +1,5 @@
|
|
1
|
+
<section class="bg-white p-5 p-md-7 p-lg-9 mb-5 mb-md-6 shadow rounded-3">
|
2
|
+
<%= rails_admin_form_for @object, url: edit_path(@abstract_model, @object.id), as: @abstract_model.param_key, html: { method: "put", multipart: true, class: "main", data: { title: @page_name } } do |form| %>
|
3
|
+
<%= form.generate action: :update %>
|
4
|
+
<% end %>
|
5
|
+
</section>
|
@@ -0,0 +1,128 @@
|
|
1
|
+
<% params = request.params.except(:action, :controller, :utf8, :page, :per_page, :format, :authenticity_token) %>
|
2
|
+
<% visible_fields = @model_config.export.with(view: self, object: @abstract_model.model.new, controller: self.controller).visible_fields %>
|
3
|
+
<%= form_tag export_path(params.merge(all: true)), method: 'post', class: "main", data: {turbo: false} do %>
|
4
|
+
<section class="bg-white p-4 p-md-5 mb-5 mb-md-6 shadow rounded-3">
|
5
|
+
<input name="send_data" type="hidden" value="true" />
|
6
|
+
<fieldset id="fields_to_export" class="mb-5">
|
7
|
+
<h2 class="h4 mb-0">
|
8
|
+
<legend class="mb-0"><%= t('admin.export.select') %></legend>
|
9
|
+
</h2>
|
10
|
+
</fieldset>
|
11
|
+
<fieldset id="fields_to_export">
|
12
|
+
<div class="control-group">
|
13
|
+
<div class="checkbox mb-3">
|
14
|
+
<label class="form-check" for="reverse-selection">
|
15
|
+
<%= check_box_tag 'all', 'all', true, { id: 'reverse-selection', class: 'reverse-selection form-check-input' } %>
|
16
|
+
<h3 class="h6 form-check-label mb-0"><%= t('admin.export.fields_from', name: @model_config.label_plural) %></h3>
|
17
|
+
</label>
|
18
|
+
</div>
|
19
|
+
<div class="controls row gy-2">
|
20
|
+
<% visible_fields.select{ |f| !f.association? || f.association.polymorphic? }.each do |field| %>
|
21
|
+
<% list = field.virtual? ? 'methods' : 'only' %>
|
22
|
+
<div class="col-auto">
|
23
|
+
<div class="checkbox">
|
24
|
+
<% if field.association? && field.association.polymorphic? %>
|
25
|
+
<label class="form-check form-label" for="schema_<%= list %>_<%= field.method_name %>">
|
26
|
+
<%= check_box_tag "schema[#{list}][]", field.method_name, true, { id: "schema_#{list}_#{field.method_name}", class: "form-check-input" } %>
|
27
|
+
<%= field.label + " [id]" %>
|
28
|
+
</label>
|
29
|
+
<% polymorphic_type_column_name = @abstract_model.properties.detect { |p| field.association.foreign_type == p.name }.name %>
|
30
|
+
<label class="form-check form-label" for="schema_<%= list %>_<%= polymorphic_type_column_name %>">
|
31
|
+
<%= check_box_tag "schema[#{list}][]", polymorphic_type_column_name, true, { id: "schema_#{list}_#{polymorphic_type_column_name}", class: "form-check-input" } %>
|
32
|
+
<%= field.label + " [type]" %>
|
33
|
+
</label>
|
34
|
+
<% else %>
|
35
|
+
<label class="form-check form-label" for="schema_<%= list %>_<%= field.name %>">
|
36
|
+
<%= check_box_tag "schema[#{list}][]", field.name, true, { id: "schema_#{list}_#{field.name}", class: "form-check-input" } %>
|
37
|
+
<%= field.label %>
|
38
|
+
</label>
|
39
|
+
<% end %>
|
40
|
+
</div>
|
41
|
+
</div>
|
42
|
+
<% end %>
|
43
|
+
</div>
|
44
|
+
</div>
|
45
|
+
<hr class="my-6">
|
46
|
+
<h2 class="h5 mb-5">Associations</h2>
|
47
|
+
<% visible_fields.select{ |f| f.association? && !f.association.polymorphic? }.each_with_index do |field, group_index| %>
|
48
|
+
<% fields = field.associated_model_config.export.with(controller: self.controller, view: self, object: (associated_model = field.associated_model_config.abstract_model.model).new).visible_fields.select{ |f| !f.association? } %>
|
49
|
+
<div class="control-group">
|
50
|
+
<div class="checkbox mb-3">
|
51
|
+
<label class="form-check">
|
52
|
+
<%= check_box_tag 'all', 'all', true, { id: 'reverse-selection', class: 'reverse-selection form-check-input' } %>
|
53
|
+
<h3 class="h6 form-check-label mb-0"><%= t('admin.export.fields_from_associated', name: field.label.pluralize) %></h3>
|
54
|
+
</label>
|
55
|
+
</div>
|
56
|
+
<div class="controls row gy-2">
|
57
|
+
<% fields.each do |associated_model_field| %>
|
58
|
+
<% list = associated_model_field.virtual? ? 'methods' : 'only' %>
|
59
|
+
<div class="col-auto">
|
60
|
+
<div class="checkbox">
|
61
|
+
<label class="form-check form-label" for="schema_include_<%= field.name %>_<%= list %>_<%= associated_model_field.name %>">
|
62
|
+
<%= check_box_tag "schema[include][#{field.name}][#{list}][]", associated_model_field.name, true, { id: "schema_include_#{field.name}_#{list}_#{associated_model_field.name}", class: "form-check-input" } %>
|
63
|
+
<%= associated_model_field.label %>
|
64
|
+
</label>
|
65
|
+
</div>
|
66
|
+
</div>
|
67
|
+
<% end %>
|
68
|
+
</div>
|
69
|
+
</div>
|
70
|
+
<%= "<hr class='my-6'>".html_safe if group_index < visible_fields.select{ |f| f.association? && !f.association.polymorphic? }.size - 1 %>
|
71
|
+
<% end %>
|
72
|
+
</fieldset>
|
73
|
+
</section>
|
74
|
+
<section class="bg-white p-4 p-md-5 mb-5 mb-md-6 shadow rounded-3">
|
75
|
+
<fieldset>
|
76
|
+
<h2 class="h4 d-flex mb-5">
|
77
|
+
<legend class="mb-0"><%= t('admin.export.options_for', name: 'csv') %></legend>
|
78
|
+
</h2>
|
79
|
+
<div class="control-group row mb-3">
|
80
|
+
<% guessed_encoding = @abstract_model.encoding %>
|
81
|
+
<label class="form-label" for="csv_options_encoding_to"><%= t('admin.export.csv.encoding_to') %></label>
|
82
|
+
<div class="controls col-md-11 col-xl-6">
|
83
|
+
<%= select_tag 'csv_options[encoding_to]', options_for_select(Encoding.name_list.sort), include_blank: true, placeholder: t('admin.misc.search'), :'data-enumeration' => true %>
|
84
|
+
</div>
|
85
|
+
<p class="form-text mb-0"><%= t('admin.export.csv.encoding_to_help', name: guessed_encoding) %></p>
|
86
|
+
</div>
|
87
|
+
<div class="control-group mb-3">
|
88
|
+
<div class="form-check form-switch d-flex flex-column">
|
89
|
+
<label class="form-label for="csv_options_skip_header"><%= t('admin.export.csv.skip_header') %></label>
|
90
|
+
<%= check_box_tag 'csv_options[skip_header]', 'true', false, { class: 'form-check-input' } %>
|
91
|
+
</div>
|
92
|
+
<p class="form-text mb-0"><%= t('admin.export.csv.skip_header_help') %></p>
|
93
|
+
</div>
|
94
|
+
<div class="control-group row">
|
95
|
+
<label class="form-label" for="csv_options_generator_col_sep">
|
96
|
+
<%= t('admin.export.csv.col_sep') %>
|
97
|
+
</label>
|
98
|
+
<div class="controls col-md-11 col-xl-6">
|
99
|
+
<%= select_tag 'csv_options[generator][col_sep]', options_for_select({ '' => t('admin.export.csv.default_col_sep'), "<comma> ','" => ',', "<semicolon> ';'" => ';', '<tabs>' => "'\t'" }), placeholder: t('admin.misc.search'), :'data-enumeration' => true %>
|
100
|
+
</div>
|
101
|
+
<p class="form-text mb-0"><%= t('admin.export.csv.col_sep_help', value: t('admin.export.csv.default_col_sep')) %></p>
|
102
|
+
</div>
|
103
|
+
</fieldset>
|
104
|
+
</section>
|
105
|
+
<div class="form-actions row justify-content-end gx-5 gy-3 gy-md-0">
|
106
|
+
<input name="return_to" type="<%= :hidden %>" value="<%= (params[:return_to].presence || request.referer) %>" />
|
107
|
+
<div class="col-auto">
|
108
|
+
<button class="btn btn-primary" name="csv" type="submit">
|
109
|
+
<%= t("admin.export.confirmation", name: 'csv') %>
|
110
|
+
</button>
|
111
|
+
</div>
|
112
|
+
<div class="col-auto">
|
113
|
+
<button class="btn btn-white" name="json" type="submit">
|
114
|
+
<%= t("admin.export.confirmation", name: 'json') %>
|
115
|
+
</button>
|
116
|
+
</div>
|
117
|
+
<div class="col-auto">
|
118
|
+
<button class="btn btn-white" name="xml" type="submit">
|
119
|
+
<%= t("admin.export.confirmation", name: 'xml') %>
|
120
|
+
</button>
|
121
|
+
</div>
|
122
|
+
<div class="col-auto">
|
123
|
+
<button class="btn btn-danger" name="_continue" type="submit">
|
124
|
+
<%= t("admin.form.cancel") %>
|
125
|
+
</button>
|
126
|
+
</div>
|
127
|
+
</div>
|
128
|
+
<% end %>
|