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.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +5 -0
  3. data/CODE_OF_CONDUCT.md +132 -0
  4. data/LICENSE +201 -0
  5. data/README.md +15 -0
  6. data/Rakefile +4 -0
  7. data/app/assets/javascripts/rails_admin/application.js.erb +32 -0
  8. data/app/assets/javascripts/rails_admin/custom/filtering-select.js +304 -0
  9. data/app/assets/javascripts/rails_admin/custom/sidescroll.js +20 -0
  10. data/app/assets/javascripts/rails_admin/custom/ui.js +11 -0
  11. data/app/assets/stylesheets/rails_admin/application.scss.erb +52 -0
  12. data/app/assets/stylesheets/rails_admin/custom/base/base.scss +5 -0
  13. data/app/assets/stylesheets/rails_admin/custom/base/bootstrap-variables.scss +196 -0
  14. data/app/assets/stylesheets/rails_admin/custom/base/mixins.scss +88 -0
  15. data/app/assets/stylesheets/rails_admin/custom/base/typography.scss +16 -0
  16. data/app/assets/stylesheets/rails_admin/custom/base/variables.scss +86 -0
  17. data/app/assets/stylesheets/rails_admin/custom/components/breadcrumbs.scss +18 -0
  18. data/app/assets/stylesheets/rails_admin/custom/components/buttons.scss +55 -0
  19. data/app/assets/stylesheets/rails_admin/custom/components/dropdowns.scss +40 -0
  20. data/app/assets/stylesheets/rails_admin/custom/components/filtering-select.scss +43 -0
  21. data/app/assets/stylesheets/rails_admin/custom/components/forms.scss +25 -0
  22. data/app/assets/stylesheets/rails_admin/custom/components/images.scss +21 -0
  23. data/app/assets/stylesheets/rails_admin/custom/components/navbar.scss +18 -0
  24. data/app/assets/stylesheets/rails_admin/custom/components/navs.scss +28 -0
  25. data/app/assets/stylesheets/rails_admin/custom/components/offcanvas.scss +18 -0
  26. data/app/assets/stylesheets/rails_admin/custom/components/progress.scss +11 -0
  27. data/app/assets/stylesheets/rails_admin/custom/components/sidebar.scss +115 -0
  28. data/app/assets/stylesheets/rails_admin/custom/components/table.scss +59 -0
  29. data/app/assets/stylesheets/rails_admin/custom/components/tiles.scss +20 -0
  30. data/app/assets/stylesheets/rails_admin/custom/layouts/body.scss +10 -0
  31. data/app/assets/stylesheets/rails_admin/custom/layouts/containers.scss +31 -0
  32. data/app/helpers/rails_admin/application_helper.rb +270 -0
  33. data/app/views/layouts/rails_admin/_sidebar_navigation.html.erb +1 -0
  34. data/app/views/layouts/rails_admin/application.html.erb +24 -0
  35. data/app/views/layouts/rails_admin/content.html.erb +57 -0
  36. data/app/views/layouts/rails_admin/custom/_nav.html.erb +10 -0
  37. data/app/views/rails_admin/main/_submit_buttons.html.erb +25 -0
  38. data/app/views/rails_admin/main/dashboard.html.erb +70 -0
  39. data/app/views/rails_admin/main/delete.html.erb +19 -0
  40. data/app/views/rails_admin/main/edit.html.erb +5 -0
  41. data/app/views/rails_admin/main/export.html.erb +128 -0
  42. data/app/views/rails_admin/main/index.html.erb +170 -0
  43. data/app/views/rails_admin/main/new.html.erb +5 -0
  44. data/app/views/rails_admin/main/show.html.erb +28 -0
  45. data/lib/swop/engine.rb +7 -0
  46. data/lib/swop/version.rb +5 -0
  47. data/lib/swop.rb +8 -0
  48. data/sig/swop.rbs +4 -0
  49. 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 %>