admin_suite 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/.gitignore +10 -0
- data/Gemfile +13 -0
- data/LICENSE.txt +22 -0
- data/README.md +7 -0
- data/Rakefile +11 -0
- data/app/assets/admin_suite.css +444 -0
- data/app/assets/admin_suite_tailwind.css +8 -0
- data/app/assets/builds/admin_suite_tailwind.css +8 -0
- data/app/assets/rouge.css +218 -0
- data/app/assets/tailwind/admin_suite.css +22 -0
- data/app/controllers/admin_suite/application_controller.rb +118 -0
- data/app/controllers/admin_suite/dashboard_controller.rb +258 -0
- data/app/controllers/admin_suite/docs_controller.rb +155 -0
- data/app/controllers/admin_suite/portals_controller.rb +22 -0
- data/app/controllers/admin_suite/resources_controller.rb +238 -0
- data/app/helpers/admin_suite/base_helper.rb +1199 -0
- data/app/helpers/admin_suite/icon_helper.rb +61 -0
- data/app/helpers/admin_suite/panels_helper.rb +52 -0
- data/app/helpers/admin_suite/resources_helper.rb +15 -0
- data/app/helpers/admin_suite/theme_helper.rb +99 -0
- data/app/javascript/controllers/admin_suite/click_actions_controller.js +73 -0
- data/app/javascript/controllers/admin_suite/clipboard_controller.js +57 -0
- data/app/javascript/controllers/admin_suite/code_editor_controller.js +45 -0
- data/app/javascript/controllers/admin_suite/file_upload_controller.js +238 -0
- data/app/javascript/controllers/admin_suite/json_editor_controller.js +62 -0
- data/app/javascript/controllers/admin_suite/live_filter_controller.js +71 -0
- data/app/javascript/controllers/admin_suite/markdown_editor_controller.js +67 -0
- data/app/javascript/controllers/admin_suite/searchable_select_controller.js +171 -0
- data/app/javascript/controllers/admin_suite/sidebar_controller.js +33 -0
- data/app/javascript/controllers/admin_suite/tag_select_controller.js +193 -0
- data/app/javascript/controllers/admin_suite/toggle_switch_controller.js +66 -0
- data/app/views/admin_suite/dashboard/index.html.erb +21 -0
- data/app/views/admin_suite/docs/index.html.erb +86 -0
- data/app/views/admin_suite/panels/_cards.html.erb +107 -0
- data/app/views/admin_suite/panels/_chart.html.erb +47 -0
- data/app/views/admin_suite/panels/_health.html.erb +44 -0
- data/app/views/admin_suite/panels/_recent.html.erb +56 -0
- data/app/views/admin_suite/panels/_stat.html.erb +64 -0
- data/app/views/admin_suite/panels/_table.html.erb +36 -0
- data/app/views/admin_suite/portals/show.html.erb +75 -0
- data/app/views/admin_suite/resources/_form.html.erb +32 -0
- data/app/views/admin_suite/resources/edit.html.erb +24 -0
- data/app/views/admin_suite/resources/index.html.erb +315 -0
- data/app/views/admin_suite/resources/new.html.erb +22 -0
- data/app/views/admin_suite/resources/show.html.erb +184 -0
- data/app/views/admin_suite/shared/_flash.html.erb +30 -0
- data/app/views/admin_suite/shared/_form.html.erb +60 -0
- data/app/views/admin_suite/shared/_json_editor_field.html.erb +52 -0
- data/app/views/admin_suite/shared/_sidebar.html.erb +94 -0
- data/app/views/admin_suite/shared/_toggle_cell.html.erb +34 -0
- data/app/views/admin_suite/shared/_topbar.html.erb +47 -0
- data/app/views/layouts/admin_suite/application.html.erb +79 -0
- data/lib/admin/base/action_executor.rb +155 -0
- data/lib/admin/base/action_handler.rb +31 -0
- data/lib/admin/base/filter_builder.rb +121 -0
- data/lib/admin/base/resource.rb +541 -0
- data/lib/admin_suite/configuration.rb +42 -0
- data/lib/admin_suite/engine.rb +101 -0
- data/lib/admin_suite/markdown_renderer.rb +115 -0
- data/lib/admin_suite/portal_definition.rb +64 -0
- data/lib/admin_suite/portal_registry.rb +32 -0
- data/lib/admin_suite/theme_palette.rb +36 -0
- data/lib/admin_suite/ui/dashboard_definition.rb +69 -0
- data/lib/admin_suite/ui/field_renderer_registry.rb +119 -0
- data/lib/admin_suite/ui/form_field_renderer.rb +48 -0
- data/lib/admin_suite/ui/show_formatter_registry.rb +120 -0
- data/lib/admin_suite/ui/show_value_formatter.rb +70 -0
- data/lib/admin_suite/version.rb +10 -0
- data/lib/admin_suite.rb +54 -0
- data/lib/generators/admin_suite/install/install_generator.rb +23 -0
- data/lib/generators/admin_suite/install/templates/admin_suite.rb +60 -0
- data/lib/generators/admin_suite/resource/resource_generator.rb +83 -0
- data/lib/generators/admin_suite/resource/templates/resource.rb.tt +47 -0
- data/lib/generators/admin_suite/scaffold/scaffold_generator.rb +28 -0
- data/lib/tasks/admin_suite_tailwind.rake +28 -0
- data/lib/tasks/admin_suite_test.rake +11 -0
- data/test/dummy/Gemfile +21 -0
- data/test/dummy/README.md +24 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/stylesheets/application.css +10 -0
- data/test/dummy/app/controllers/application_controller.rb +4 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/models/application_record.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +28 -0
- data/test/dummy/app/views/pwa/manifest.json.erb +22 -0
- data/test/dummy/app/views/pwa/service-worker.js +26 -0
- data/test/dummy/bin/ci +6 -0
- data/test/dummy/bin/dev +2 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/bin/setup +35 -0
- data/test/dummy/config/application.rb +43 -0
- data/test/dummy/config/boot.rb +3 -0
- data/test/dummy/config/ci.rb +19 -0
- data/test/dummy/config/database.yml +31 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +57 -0
- data/test/dummy/config/environments/production.rb +67 -0
- data/test/dummy/config/environments/test.rb +42 -0
- data/test/dummy/config/initializers/assets.rb +7 -0
- data/test/dummy/config/initializers/content_security_policy.rb +29 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +8 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/locales/en.yml +31 -0
- data/test/dummy/config/puma.rb +39 -0
- data/test/dummy/config/routes.rb +16 -0
- data/test/dummy/config.ru +6 -0
- data/test/dummy/db/seeds.rb +9 -0
- data/test/dummy/log/test.log +441 -0
- data/test/dummy/public/400.html +135 -0
- data/test/dummy/public/404.html +135 -0
- data/test/dummy/public/406-unsupported-browser.html +135 -0
- data/test/dummy/public/422.html +135 -0
- data/test/dummy/public/500.html +135 -0
- data/test/dummy/public/icon.png +0 -0
- data/test/dummy/public/icon.svg +3 -0
- data/test/dummy/public/robots.txt +1 -0
- data/test/dummy/test/test_helper.rb +15 -0
- data/test/dummy/tmp/local_secret.txt +1 -0
- data/test/fixtures/docs/progress/PROGRESS_REPORT.md +6 -0
- data/test/integration/dashboard_test.rb +13 -0
- data/test/integration/docs_test.rb +46 -0
- data/test/integration/theme_test.rb +27 -0
- data/test/lib/markdown_renderer_test.rb +20 -0
- data/test/lib/theme_palette_test.rb +24 -0
- data/test/test_helper.rb +11 -0
- metadata +264 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AdminSuite
|
|
4
|
+
class DocsController < ApplicationController
|
|
5
|
+
DocNotFound = Class.new(StandardError)
|
|
6
|
+
|
|
7
|
+
before_action :set_docs_root
|
|
8
|
+
|
|
9
|
+
# GET /docs
|
|
10
|
+
# GET /docs?path=relative/path.md
|
|
11
|
+
def index
|
|
12
|
+
@files = grouped_markdown_files
|
|
13
|
+
@selected_path = params[:path].presence
|
|
14
|
+
|
|
15
|
+
if @selected_path.present?
|
|
16
|
+
load_doc_content(@selected_path)
|
|
17
|
+
elsif @files.values.flatten.any?
|
|
18
|
+
@selected_path = @files.values.flatten.first
|
|
19
|
+
load_doc_content(@selected_path)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# GET /docs/*path
|
|
24
|
+
def show
|
|
25
|
+
relative_path = params[:path].to_s
|
|
26
|
+
if params[:format].present? && !relative_path.end_with?(".#{params[:format]}")
|
|
27
|
+
relative_path = "#{relative_path}.#{params[:format]}"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Even when the doc path ends with `.md`, we always render HTML.
|
|
31
|
+
request.format = :html
|
|
32
|
+
file_path = resolve_doc_path!(relative_path)
|
|
33
|
+
|
|
34
|
+
@files = grouped_markdown_files
|
|
35
|
+
@selected_path = relative_path
|
|
36
|
+
@title = File.basename(file_path, ".md").tr("_", " ").tr("-", " ").titleize
|
|
37
|
+
@raw_markdown = File.read(file_path)
|
|
38
|
+
|
|
39
|
+
rendered = markdown_renderer.new(@raw_markdown).render
|
|
40
|
+
@content_html = rendered[:html]
|
|
41
|
+
@toc = rendered[:toc]
|
|
42
|
+
@reading_time = rendered[:reading_time_minutes]
|
|
43
|
+
|
|
44
|
+
render :index, formats: [ :html ]
|
|
45
|
+
rescue DocNotFound
|
|
46
|
+
redirect_to docs_path, alert: "Doc not found."
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def set_docs_root
|
|
52
|
+
@docs_root = docs_root
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def load_doc_content(relative_path)
|
|
56
|
+
file_path = resolve_doc_path!(relative_path)
|
|
57
|
+
@title = File.basename(file_path, ".md").tr("_", " ").tr("-", " ").titleize
|
|
58
|
+
@raw_markdown = File.read(file_path)
|
|
59
|
+
|
|
60
|
+
rendered = markdown_renderer.new(@raw_markdown).render
|
|
61
|
+
@content_html = rendered[:html]
|
|
62
|
+
@toc = rendered[:toc]
|
|
63
|
+
@reading_time = rendered[:reading_time_minutes]
|
|
64
|
+
rescue DocNotFound
|
|
65
|
+
@title = nil
|
|
66
|
+
@content_html = nil
|
|
67
|
+
@toc = []
|
|
68
|
+
@reading_time = nil
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def markdown_renderer
|
|
72
|
+
AdminSuite::MarkdownRenderer
|
|
73
|
+
rescue NameError
|
|
74
|
+
# In development, new engine lib files can be added without a server restart.
|
|
75
|
+
# Make the docs viewer resilient by loading the renderer on demand.
|
|
76
|
+
require "admin_suite/markdown_renderer"
|
|
77
|
+
AdminSuite::MarkdownRenderer
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def grouped_markdown_files
|
|
81
|
+
base = docs_root_realpath
|
|
82
|
+
files = Dir.glob(base.join("**/*.md")).sort.map do |abs|
|
|
83
|
+
abs_path = Pathname.new(abs)
|
|
84
|
+
abs_path.relative_path_from(base).to_s
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
groups = files.group_by { |path| group_name_for_path(path) }
|
|
88
|
+
groups.sort_by { |k, _| k.to_s }.to_h
|
|
89
|
+
rescue StandardError
|
|
90
|
+
{}
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def group_name_for_path(relative_path)
|
|
94
|
+
folder = relative_path.to_s.split(File::SEPARATOR).first
|
|
95
|
+
if folder.present? && folder != File.basename(relative_path.to_s)
|
|
96
|
+
return humanize_folder_name(folder)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
"Docs"
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def humanize_folder_name(folder)
|
|
103
|
+
normalized = folder.to_s.tr("_", " ").tr("-", " ").strip
|
|
104
|
+
acronyms = {
|
|
105
|
+
"cicd" => "CICD",
|
|
106
|
+
"ci cd" => "CICD",
|
|
107
|
+
"ai" => "AI",
|
|
108
|
+
"ops" => "Ops",
|
|
109
|
+
"oauth" => "OAuth",
|
|
110
|
+
"ui" => "UI",
|
|
111
|
+
"ux" => "UX",
|
|
112
|
+
"api" => "API"
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
key = normalized.downcase
|
|
116
|
+
return acronyms[key] if acronyms.key?(key)
|
|
117
|
+
|
|
118
|
+
normalized.titleize
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def docs_root
|
|
122
|
+
value =
|
|
123
|
+
if AdminSuite.config.respond_to?(:docs_path)
|
|
124
|
+
AdminSuite.config.docs_path
|
|
125
|
+
else
|
|
126
|
+
Rails.root.join("docs")
|
|
127
|
+
end
|
|
128
|
+
value = value.call(self) if value.respond_to?(:call)
|
|
129
|
+
value = Rails.root.join("docs") if value.blank?
|
|
130
|
+
Pathname.new(value.to_s)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def resolve_doc_path!(relative_path)
|
|
134
|
+
raise DocNotFound if relative_path.blank?
|
|
135
|
+
raise DocNotFound if relative_path.include?("..")
|
|
136
|
+
|
|
137
|
+
base = docs_root_realpath
|
|
138
|
+
candidate = base.join(relative_path)
|
|
139
|
+
raise DocNotFound unless candidate.extname == ".md"
|
|
140
|
+
|
|
141
|
+
real = candidate.realpath
|
|
142
|
+
raise DocNotFound unless real.to_s.start_with?(base.to_s + File::SEPARATOR)
|
|
143
|
+
|
|
144
|
+
real.to_s
|
|
145
|
+
rescue Errno::ENOENT, Errno::EACCES
|
|
146
|
+
raise DocNotFound
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def docs_root_realpath
|
|
150
|
+
docs_root.realpath
|
|
151
|
+
rescue Errno::ENOENT
|
|
152
|
+
docs_root
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AdminSuite
|
|
4
|
+
class PortalsController < ApplicationController
|
|
5
|
+
def show
|
|
6
|
+
ensure_portals_loaded!
|
|
7
|
+
@portal_key = params[:portal].to_s.presence&.to_sym
|
|
8
|
+
@portal = navigation_items[@portal_key]
|
|
9
|
+
@portal_definition = AdminSuite::PortalRegistry.fetch(@portal_key)
|
|
10
|
+
|
|
11
|
+
raise ActionController::RoutingError, "Portal not found" if @portal.blank?
|
|
12
|
+
|
|
13
|
+
@sections =
|
|
14
|
+
(@portal[:sections] || {}).sort_by { |(_k, s)| s[:label].to_s }.map do |section_key, section|
|
|
15
|
+
items = Array(section[:items]).sort_by { |it| it[:label].to_s }
|
|
16
|
+
[ section_key, section.merge(items: items) ]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
@dashboard_rows = @portal_definition&.dashboard_definition&.rows
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AdminSuite
|
|
4
|
+
class ResourcesController < ApplicationController
|
|
5
|
+
include Pagy::Backend
|
|
6
|
+
include Pagy::Frontend
|
|
7
|
+
|
|
8
|
+
before_action :set_resource, if: -> { params[:id].present? && !%w[index new create].include?(action_name) }
|
|
9
|
+
|
|
10
|
+
helper_method :resource_config, :resource_class, :resource, :collection, :current_portal, :resource_name
|
|
11
|
+
|
|
12
|
+
# GET /:portal/:resource_name
|
|
13
|
+
def index
|
|
14
|
+
@stats = calculate_stats if resource_config&.index_config&.stats_list&.any?
|
|
15
|
+
@pagy, @collection = paginate_collection(filtered_collection)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# GET /:portal/:resource_name/:id
|
|
19
|
+
def show
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# GET /:portal/:resource_name/new
|
|
23
|
+
def new
|
|
24
|
+
@resource = resource_class.new
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# GET /:portal/:resource_name/:id/edit
|
|
28
|
+
def edit
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# POST /:portal/:resource_name
|
|
32
|
+
def create
|
|
33
|
+
@resource = resource_class.new(resource_params)
|
|
34
|
+
if @resource.save
|
|
35
|
+
redirect_to resource_url(@resource), notice: "#{resource_config.human_name} was successfully created."
|
|
36
|
+
else
|
|
37
|
+
render :new, status: :unprocessable_entity
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# PATCH/PUT /:portal/:resource_name/:id
|
|
42
|
+
def update
|
|
43
|
+
if @resource.update(resource_params)
|
|
44
|
+
redirect_to resource_url(@resource), notice: "#{resource_config.human_name} was successfully updated."
|
|
45
|
+
else
|
|
46
|
+
render :edit, status: :unprocessable_entity
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# DELETE /:portal/:resource_name/:id
|
|
51
|
+
def destroy
|
|
52
|
+
@resource.destroy!
|
|
53
|
+
redirect_to collection_url, notice: "#{resource_config.human_name} was successfully deleted.", status: :see_other
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# POST /:portal/:resource_name/:id/execute_action/:action_name
|
|
57
|
+
def execute_action
|
|
58
|
+
action = params[:action_name].to_s.to_sym
|
|
59
|
+
action_def = find_action(action)
|
|
60
|
+
unless action_def
|
|
61
|
+
redirect_to resource_url(@resource), alert: "Action not found."
|
|
62
|
+
return
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
executor = Admin::Base::ActionExecutor.new(resource_config, action, admin_suite_actor)
|
|
66
|
+
result = executor.execute_member(@resource, params.to_unsafe_h)
|
|
67
|
+
|
|
68
|
+
if result.success?
|
|
69
|
+
redirect_to resource_url(@resource), notice: result.message
|
|
70
|
+
else
|
|
71
|
+
redirect_to resource_url(@resource), alert: result.message
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# POST /:portal/:resource_name/bulk_action/:action_name
|
|
76
|
+
def bulk_action
|
|
77
|
+
action = params[:action_name].to_s.to_sym
|
|
78
|
+
ids = params[:ids] || []
|
|
79
|
+
if ids.empty?
|
|
80
|
+
redirect_to collection_url, alert: "No items selected."
|
|
81
|
+
return
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
model = resource_class
|
|
85
|
+
records = model.where(id: ids)
|
|
86
|
+
executor = Admin::Base::ActionExecutor.new(resource_config, action, admin_suite_actor)
|
|
87
|
+
result = executor.execute_bulk(records, params.to_unsafe_h)
|
|
88
|
+
|
|
89
|
+
if result.success?
|
|
90
|
+
redirect_to collection_url, notice: result.message
|
|
91
|
+
else
|
|
92
|
+
redirect_to collection_url, alert: result.message
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# POST /:portal/:resource_name/:id/toggle
|
|
97
|
+
def toggle
|
|
98
|
+
field = params[:field].presence&.to_sym
|
|
99
|
+
unless field
|
|
100
|
+
head :unprocessable_entity
|
|
101
|
+
return
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
unless toggleable_fields.include?(field)
|
|
105
|
+
head :unprocessable_entity
|
|
106
|
+
return
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
current_value = !!@resource.public_send(field)
|
|
110
|
+
@resource.update!(field => !current_value)
|
|
111
|
+
|
|
112
|
+
respond_to do |format|
|
|
113
|
+
format.turbo_stream do
|
|
114
|
+
render turbo_stream: turbo_stream.replace(
|
|
115
|
+
dom_id(@resource, :toggle),
|
|
116
|
+
partial: "admin_suite/shared/toggle_cell",
|
|
117
|
+
locals: { record: @resource, field: field, toggle_url: resource_toggle_path(portal: current_portal, resource_name: resource_name, id: @resource.to_param, field: field) }
|
|
118
|
+
)
|
|
119
|
+
end
|
|
120
|
+
format.html { redirect_to resource_url(@resource), notice: "#{resource_config.human_name} updated." }
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
private
|
|
125
|
+
|
|
126
|
+
def current_portal
|
|
127
|
+
params[:portal].to_s.presence&.to_sym
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def resource_name
|
|
131
|
+
params[:resource_name].to_s
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def resource_config
|
|
135
|
+
ensure_resources_loaded!
|
|
136
|
+
klass_name = resource_name.singularize.camelize
|
|
137
|
+
"Admin::Resources::#{klass_name}Resource".constantize
|
|
138
|
+
rescue NameError
|
|
139
|
+
nil
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def resource_class
|
|
143
|
+
resource_config&.model_class || resource_name.classify.constantize
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def set_resource
|
|
147
|
+
@resource = resource_class.find(params[:id])
|
|
148
|
+
rescue ActiveRecord::RecordNotFound
|
|
149
|
+
# Support "friendly" params (e.g. slugged records) without requiring host apps
|
|
150
|
+
# to change their model primary keys.
|
|
151
|
+
id = params[:id].to_s
|
|
152
|
+
columns = resource_class.column_names
|
|
153
|
+
|
|
154
|
+
@resource =
|
|
155
|
+
if columns.include?("slug")
|
|
156
|
+
resource_class.find_by!(slug: id)
|
|
157
|
+
elsif columns.include?("uuid")
|
|
158
|
+
resource_class.find_by!(uuid: id)
|
|
159
|
+
elsif columns.include?("token")
|
|
160
|
+
resource_class.find_by!(token: id)
|
|
161
|
+
else
|
|
162
|
+
raise
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def resource
|
|
167
|
+
@resource
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def collection
|
|
171
|
+
@collection
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def filtered_collection
|
|
175
|
+
return resource_class.all unless resource_config&.index_config
|
|
176
|
+
|
|
177
|
+
Admin::Base::FilterBuilder.new(resource_config, params).apply(resource_class.all)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def paginate_collection(scope)
|
|
181
|
+
per_page = resource_config&.index_config&.per_page || 25
|
|
182
|
+
pagy(scope, items: per_page)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def calculate_stats
|
|
186
|
+
resource_config.index_config.stats_list.map do |stat_def|
|
|
187
|
+
value =
|
|
188
|
+
begin
|
|
189
|
+
stat_def.calculator.call
|
|
190
|
+
rescue StandardError
|
|
191
|
+
"N/A"
|
|
192
|
+
end
|
|
193
|
+
{ name: stat_def.name.to_s.humanize, value: value, color: stat_def.color }
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def find_action(name)
|
|
198
|
+
resource_config&.actions_config&.member_actions&.find { |a| a.name == name }
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def resource_params
|
|
202
|
+
permitted_fields = []
|
|
203
|
+
array_fields = []
|
|
204
|
+
|
|
205
|
+
resource_config&.form_config&.fields_list&.each do |field|
|
|
206
|
+
next unless field.is_a?(Admin::Base::Resource::FieldDefinition)
|
|
207
|
+
|
|
208
|
+
if field.type == :tags || field.type == :multi_select
|
|
209
|
+
array_fields << { field.name => [] }
|
|
210
|
+
array_fields << { tag_list: [] } if field.type == :tags && field.name != :tag_list
|
|
211
|
+
else
|
|
212
|
+
permitted_fields << field.name
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
key = resource_class.model_name.param_key
|
|
217
|
+
params.require(key).permit(permitted_fields + array_fields)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def toggleable_fields
|
|
221
|
+
return [] unless resource_config&.index_config&.columns_list
|
|
222
|
+
|
|
223
|
+
resource_config.index_config.columns_list.filter_map do |col|
|
|
224
|
+
next unless col.type == :toggle
|
|
225
|
+
|
|
226
|
+
(col.toggle_field || col.name).to_sym
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def resource_url(record)
|
|
231
|
+
resource_path(portal: current_portal, resource_name: resource_name, id: record.to_param)
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def collection_url
|
|
235
|
+
resources_path(portal: current_portal, resource_name: resource_name)
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
end
|