easy-admin-rails 0.1.14 → 0.2.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 +4 -4
- data/app/assets/builds/easy_admin.base.js +254 -18
- data/app/assets/builds/easy_admin.base.js.map +4 -4
- data/app/assets/builds/easy_admin.css +112 -18
- data/app/components/easy_admin/base_component.rb +1 -0
- data/app/components/easy_admin/form_tabs_component.rb +5 -2
- data/app/components/easy_admin/navbar_component.rb +5 -1
- data/app/components/easy_admin/permissions/user_role_assignment_component.rb +254 -0
- data/app/components/easy_admin/permissions/user_role_permissions_component.rb +186 -0
- data/app/components/easy_admin/resources/index_component.rb +1 -4
- data/app/components/easy_admin/sidebar_component.rb +67 -2
- data/app/components/easy_admin/versions/diff_modal_component.rb +5 -1
- data/app/controllers/easy_admin/application_controller.rb +131 -1
- data/app/controllers/easy_admin/batch_actions_controller.rb +27 -0
- data/app/controllers/easy_admin/concerns/belongs_to_editing.rb +201 -0
- data/app/controllers/easy_admin/concerns/inline_field_editing.rb +297 -0
- data/app/controllers/easy_admin/concerns/resource_authorization.rb +55 -0
- data/app/controllers/easy_admin/concerns/resource_filtering.rb +178 -0
- data/app/controllers/easy_admin/concerns/resource_loading.rb +149 -0
- data/app/controllers/easy_admin/concerns/resource_pagination.rb +135 -0
- data/app/controllers/easy_admin/dashboard_controller.rb +2 -1
- data/app/controllers/easy_admin/dashboards_controller.rb +6 -40
- data/app/controllers/easy_admin/resources_controller.rb +13 -762
- data/app/controllers/easy_admin/row_actions_controller.rb +25 -0
- data/app/helpers/easy_admin/fields_helper.rb +61 -9
- data/app/javascript/easy_admin/controllers/event_emitter_controller.js +2 -4
- data/app/javascript/easy_admin/controllers/infinite_scroll_controller.js +0 -10
- data/app/javascript/easy_admin/controllers/jsoneditor_controller.js +1 -4
- data/app/javascript/easy_admin/controllers/permission_toggle_controller.js +227 -0
- data/app/javascript/easy_admin/controllers/role_preview_controller.js +93 -0
- data/app/javascript/easy_admin/controllers/select_field_controller.js +1 -2
- data/app/javascript/easy_admin/controllers/settings_button_controller.js +1 -2
- data/app/javascript/easy_admin/controllers/settings_sidebar_controller.js +1 -4
- data/app/javascript/easy_admin/controllers/turbo_stream_redirect.js +0 -2
- data/app/javascript/easy_admin/controllers.js +5 -1
- data/app/models/easy_admin/admin_user.rb +6 -0
- data/app/policies/admin_user_policy.rb +36 -0
- data/app/policies/application_policy.rb +83 -0
- data/app/views/easy_admin/application/authorization_failure.turbo_stream.erb +8 -0
- data/app/views/easy_admin/dashboards/card.html.erb +5 -0
- data/app/views/easy_admin/dashboards/card.turbo_stream.erb +7 -0
- data/app/views/easy_admin/dashboards/card_error.html.erb +3 -0
- data/app/views/easy_admin/dashboards/card_error.turbo_stream.erb +5 -0
- data/app/views/easy_admin/dashboards/show.turbo_stream.erb +7 -0
- data/app/views/easy_admin/resources/belongs_to_edit_attached.html.erb +6 -0
- data/app/views/easy_admin/resources/belongs_to_edit_attached.turbo_stream.erb +8 -0
- data/app/views/easy_admin/resources/belongs_to_reattach.html.erb +5 -0
- data/app/views/easy_admin/resources/edit.html.erb +1 -1
- data/app/views/easy_admin/resources/edit_field.html.erb +5 -0
- data/app/views/easy_admin/resources/edit_field.turbo_stream.erb +7 -0
- data/app/views/easy_admin/resources/index.html.erb +1 -1
- data/app/views/easy_admin/resources/index_frame.html.erb +8 -142
- data/app/views/easy_admin/resources/update_belongs_to_attached.turbo_stream.erb +25 -0
- data/app/views/layouts/easy_admin/application.html.erb +15 -2
- data/config/initializers/easy_admin_permissions.rb +73 -0
- data/db/seeds/easy_admin_permissions.rb +121 -0
- data/lib/easy-admin-rails.rb +2 -0
- data/lib/easy_admin/permissions/component.rb +168 -0
- data/lib/easy_admin/permissions/configuration.rb +37 -0
- data/lib/easy_admin/permissions/controller.rb +164 -0
- data/lib/easy_admin/permissions/dsl.rb +180 -0
- data/lib/easy_admin/permissions/models.rb +44 -0
- data/lib/easy_admin/permissions/permission_denied_component.rb +121 -0
- data/lib/easy_admin/permissions/resource_permissions.rb +231 -0
- data/lib/easy_admin/permissions/role_definition.rb +45 -0
- data/lib/easy_admin/permissions/role_denied_component.rb +159 -0
- data/lib/easy_admin/permissions/role_dsl.rb +73 -0
- data/lib/easy_admin/permissions/user_extensions.rb +129 -0
- data/lib/easy_admin/permissions.rb +113 -0
- data/lib/easy_admin/resource/base.rb +119 -0
- data/lib/easy_admin/resource/configuration.rb +148 -0
- data/lib/easy_admin/resource/dsl.rb +117 -0
- data/lib/easy_admin/resource/field_registry.rb +189 -0
- data/lib/easy_admin/resource/form_builder.rb +123 -0
- data/lib/easy_admin/resource/layout_builder.rb +249 -0
- data/lib/easy_admin/resource/scope_manager.rb +252 -0
- data/lib/easy_admin/resource/show_builder.rb +359 -0
- data/lib/easy_admin/resource.rb +8 -835
- data/lib/easy_admin/resource_modules.rb +11 -0
- data/lib/easy_admin/version.rb +1 -1
- data/lib/generators/easy_admin/permissions/install_generator.rb +90 -0
- data/lib/generators/easy_admin/permissions/templates/initializers/permissions.rb +37 -0
- data/lib/generators/easy_admin/permissions/templates/migrations/create_permission_tables.rb +27 -0
- data/lib/generators/easy_admin/permissions/templates/migrations/update_users_for_permissions.rb +6 -0
- data/lib/generators/easy_admin/permissions/templates/models/permission.rb +9 -0
- data/lib/generators/easy_admin/permissions/templates/models/role.rb +9 -0
- data/lib/generators/easy_admin/permissions/templates/models/role_permission.rb +9 -0
- data/lib/generators/easy_admin/permissions/templates/models/user_role.rb +9 -0
- data/lib/generators/easy_admin/permissions/templates/policies/application_policy.rb +47 -0
- data/lib/generators/easy_admin/permissions/templates/policies/user_policy.rb +36 -0
- data/lib/generators/easy_admin/permissions/templates/seeds/permissions.rb +89 -0
- metadata +62 -5
- data/db/migrate/20250101000001_create_easy_admin_admin_users.rb +0 -45
@@ -0,0 +1,178 @@
|
|
1
|
+
module EasyAdmin
|
2
|
+
module Concerns
|
3
|
+
# ResourceFiltering concern handles all filtering, searching, and scoping logic
|
4
|
+
# Provides methods for applying scopes, search, period filters, and sorting
|
5
|
+
module ResourceFiltering
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
# Determine the current scope based on params and resource configuration
|
11
|
+
def determine_current_scope
|
12
|
+
if params[:scope].present?
|
13
|
+
scope_config = @resource_class.scopes.find { |s| s[:name].to_s == params[:scope].to_s }
|
14
|
+
scope_config ? scope_config[:name] : @resource_class.default_scope&.dig(:name)
|
15
|
+
else
|
16
|
+
@resource_class.default_scope&.dig(:name)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Apply scope filtering to records
|
21
|
+
def apply_scope(records)
|
22
|
+
return records unless @resource_class.has_scopes? && @current_scope
|
23
|
+
|
24
|
+
scope_config = @resource_class.scopes.find { |s| s[:name] == @current_scope }
|
25
|
+
|
26
|
+
if scope_config && scope_config[:name] != :all
|
27
|
+
scope_method = scope_config[:scope_method]
|
28
|
+
if records.respond_to?(scope_method)
|
29
|
+
records.public_send(scope_method)
|
30
|
+
else
|
31
|
+
records
|
32
|
+
end
|
33
|
+
else
|
34
|
+
records
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Calculate counts for all scopes (for scope filters UI)
|
39
|
+
def calculate_scope_counts
|
40
|
+
counts = {}
|
41
|
+
|
42
|
+
@resource_class.scopes.each do |scope_config|
|
43
|
+
scope_name = scope_config[:name]
|
44
|
+
|
45
|
+
if scope_name == :all
|
46
|
+
counts[scope_name] = @resource_class.model_class.count
|
47
|
+
else
|
48
|
+
scope_method = scope_config[:scope_method]
|
49
|
+
if @resource_class.model_class.respond_to?(scope_method)
|
50
|
+
counts[scope_name] = @resource_class.model_class.public_send(scope_method).count
|
51
|
+
else
|
52
|
+
counts[scope_name] = 0
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
counts
|
58
|
+
end
|
59
|
+
|
60
|
+
# Apply period-based filtering (today, 7d, 30d, etc.)
|
61
|
+
def apply_period_filter(records)
|
62
|
+
return records unless params[:period].present? && records.respond_to?(:where)
|
63
|
+
|
64
|
+
date_range = case params[:period]
|
65
|
+
when 'today'
|
66
|
+
Date.current.beginning_of_day..Date.current.end_of_day
|
67
|
+
when '7d'
|
68
|
+
7.days.ago.beginning_of_day..Date.current.end_of_day
|
69
|
+
when '30d'
|
70
|
+
30.days.ago.beginning_of_day..Date.current.end_of_day
|
71
|
+
when '90d'
|
72
|
+
90.days.ago.beginning_of_day..Date.current.end_of_day
|
73
|
+
when '1y'
|
74
|
+
1.year.ago.beginning_of_day..Date.current.end_of_day
|
75
|
+
else
|
76
|
+
return records
|
77
|
+
end
|
78
|
+
|
79
|
+
# Try to filter by created_at, updated_at, or the first timestamp field
|
80
|
+
timestamp_field = determine_timestamp_field(records)
|
81
|
+
return records unless timestamp_field
|
82
|
+
|
83
|
+
records.where(timestamp_field => date_range)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Apply sorting to records
|
87
|
+
def apply_sorting(records, sort_field, sort_direction)
|
88
|
+
# Always apply sorting if sort_field is present
|
89
|
+
return records unless sort_field.present?
|
90
|
+
|
91
|
+
# Get the model class from the relation
|
92
|
+
model_class = records.respond_to?(:klass) ? records.klass : records.class
|
93
|
+
|
94
|
+
if records.respond_to?(:order) && model_class.column_names.include?(sort_field.to_s)
|
95
|
+
records.order("#{sort_field} #{sort_direction}")
|
96
|
+
else
|
97
|
+
records
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Check if any filtering parameters are active
|
102
|
+
def has_active_filters?
|
103
|
+
# Check if any filtering parameters are present
|
104
|
+
params[:q].present? ||
|
105
|
+
params[:search].present? ||
|
106
|
+
params[:period].present? ||
|
107
|
+
(params[:scope].present? && params[:scope].to_s != @resource_class.default_scope&.dig(:name).to_s)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Determine the best timestamp field for period filtering
|
111
|
+
def determine_timestamp_field(records)
|
112
|
+
# Get the model class from the relation or class
|
113
|
+
model_class = if records.is_a?(Class) && records < ActiveRecord::Base
|
114
|
+
# If records is already an ActiveRecord model class
|
115
|
+
records
|
116
|
+
elsif records.respond_to?(:klass)
|
117
|
+
# For ActiveRecord relations
|
118
|
+
records.klass
|
119
|
+
elsif records.respond_to?(:model)
|
120
|
+
records.model
|
121
|
+
else
|
122
|
+
records.class
|
123
|
+
end
|
124
|
+
|
125
|
+
# Try common timestamp fields in order of preference
|
126
|
+
%w[created_at updated_at published_at date timestamp].find do |field|
|
127
|
+
model_class.column_names.include?(field)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# Apply Ransack search filtering
|
132
|
+
def apply_ransack_search(records)
|
133
|
+
return records unless params[:q].present?
|
134
|
+
|
135
|
+
@search_query = records.ransack(params[:q])
|
136
|
+
@search_query.result
|
137
|
+
end
|
138
|
+
|
139
|
+
# Apply text-based search (fallback when Ransack not used)
|
140
|
+
def apply_text_search(records, sort_field, sort_direction)
|
141
|
+
return records unless params[:search].present?
|
142
|
+
|
143
|
+
@resource_class.search_records(
|
144
|
+
params[:search],
|
145
|
+
sort_field: sort_field,
|
146
|
+
sort_direction: sort_direction,
|
147
|
+
records: records
|
148
|
+
)
|
149
|
+
end
|
150
|
+
|
151
|
+
# Build the filtered and sorted record set
|
152
|
+
def build_filtered_records(sort_field, sort_direction)
|
153
|
+
# Start with base records (ensure we have a relation, not just a class)
|
154
|
+
records = apply_scope(@resource_class.model_class.all)
|
155
|
+
|
156
|
+
# Apply eager loading for index action
|
157
|
+
if @resource_class.index_includes.any?
|
158
|
+
records = records.includes(@resource_class.index_includes)
|
159
|
+
end
|
160
|
+
|
161
|
+
# Apply Ransack filtering if present
|
162
|
+
records = apply_ransack_search(records)
|
163
|
+
|
164
|
+
# Apply text search if present (fallback/additional search)
|
165
|
+
if params[:search].present?
|
166
|
+
records = apply_text_search(records, sort_field, sort_direction)
|
167
|
+
else
|
168
|
+
records = apply_sorting(records, sort_field, sort_direction)
|
169
|
+
end
|
170
|
+
|
171
|
+
# Apply period filtering
|
172
|
+
records = apply_period_filter(records)
|
173
|
+
|
174
|
+
records
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
module EasyAdmin
|
2
|
+
module Concerns
|
3
|
+
# ResourceLoading concern handles loading resource classes and individual records
|
4
|
+
# Provides before_action callbacks and helper methods for resource/record loading
|
5
|
+
module ResourceLoading
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
before_action :load_resource_class
|
10
|
+
before_action :load_record, only: [
|
11
|
+
:show, :edit, :update, :destroy,
|
12
|
+
:edit_field, :update_field,
|
13
|
+
:belongs_to_reattach, :belongs_to_edit_attached, :update_belongs_to_attached,
|
14
|
+
:versions, :revert_version, :version_diff
|
15
|
+
]
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
# Load the resource class based on the route parameter
|
21
|
+
def load_resource_class
|
22
|
+
resource_name = params[:resource_name]
|
23
|
+
@resource_class = EasyAdmin::ResourceRegistry.find_resource(resource_name)
|
24
|
+
|
25
|
+
unless @resource_class
|
26
|
+
raise ActionController::RoutingError, "Resource '#{resource_name}' not found"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Load individual record for actions that operate on a specific record
|
31
|
+
def load_record
|
32
|
+
@record = @resource_class.find_record(params[:id])
|
33
|
+
end
|
34
|
+
|
35
|
+
# Helper method to find field configuration
|
36
|
+
def find_field_config(field_name)
|
37
|
+
@resource_class.fields_config.find { |field| field[:name].to_s == field_name.to_s }
|
38
|
+
end
|
39
|
+
|
40
|
+
# Helper method to find resource class for a given model
|
41
|
+
def find_resource_class_for_model(model_class)
|
42
|
+
# Try to find the resource class by convention (e.g., User -> UserResource)
|
43
|
+
resource_class_name = "#{model_class.name}Resource"
|
44
|
+
begin
|
45
|
+
resource_class_name.constantize
|
46
|
+
rescue NameError
|
47
|
+
# If convention doesn't work, search through all resource classes
|
48
|
+
EasyAdmin.resource_registry.values.find { |rc| rc.model_class == model_class }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Get permitted attributes for a resource class
|
53
|
+
def get_permitted_attributes(resource_class)
|
54
|
+
# Get all editable field names from the resource class
|
55
|
+
permitted_attrs = []
|
56
|
+
|
57
|
+
# Check both form fields and all fields (including hidden ones)
|
58
|
+
all_fields = resource_class.form_fields + resource_class.fields_config
|
59
|
+
unique_fields = all_fields.uniq { |f| f[:name] }
|
60
|
+
|
61
|
+
Rails.logger.debug "🔍 [ResourceLoading] All fields for #{resource_class}:"
|
62
|
+
unique_fields.each_with_index do |field_config, index|
|
63
|
+
Rails.logger.debug "🔍 [ResourceLoading] Field #{index}: #{field_config[:name]} (type: #{field_config[:type]}, readonly: #{field_config[:readonly]})"
|
64
|
+
next if field_config[:readonly]
|
65
|
+
|
66
|
+
field_name = field_config[:name]
|
67
|
+
|
68
|
+
# Handle different field types that might need special parameter handling
|
69
|
+
case field_config[:type]
|
70
|
+
when :has_many
|
71
|
+
# For has_many relationships, we need to permit an array of IDs
|
72
|
+
permitted_attrs << { "#{field_name.to_s.singularize}_ids" => [] }
|
73
|
+
when :belongs_to
|
74
|
+
# For belongs_to, we need the foreign key
|
75
|
+
if field_config[:multiple]
|
76
|
+
permitted_attrs << { "#{field_name}_ids" => [] }
|
77
|
+
else
|
78
|
+
association_name = field_config[:association] || field_name
|
79
|
+
foreign_key = get_foreign_key_name(association_name)
|
80
|
+
permitted_attrs << foreign_key.to_sym
|
81
|
+
end
|
82
|
+
when :select
|
83
|
+
# For select fields with multiple option
|
84
|
+
if field_config[:multiple]
|
85
|
+
permitted_attrs << { field_name => [] }
|
86
|
+
else
|
87
|
+
permitted_attrs << field_name
|
88
|
+
end
|
89
|
+
when :json
|
90
|
+
# For JSON fields, permit nested parameters as a hash
|
91
|
+
permitted_attrs << { field_name => {} }
|
92
|
+
else
|
93
|
+
# For regular fields, check if it's the permissions_cache field which needs hash permission
|
94
|
+
if field_name == :permissions_cache
|
95
|
+
permitted_attrs << { field_name => {} }
|
96
|
+
else
|
97
|
+
permitted_attrs << field_name
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
Rails.logger.debug "🔍 [ResourceLoading] Final permitted attributes: #{permitted_attrs.inspect}"
|
103
|
+
permitted_attrs
|
104
|
+
end
|
105
|
+
|
106
|
+
# Get foreign key name for belongs_to association
|
107
|
+
def get_foreign_key_name(association_name)
|
108
|
+
model_class = @resource_class.model_class
|
109
|
+
if model_class.reflect_on_association(association_name)
|
110
|
+
model_class.reflect_on_association(association_name).foreign_key
|
111
|
+
else
|
112
|
+
"#{association_name}_id"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Get record parameters for create/update actions
|
117
|
+
def record_params
|
118
|
+
permitted_attrs = get_permitted_attributes(@resource_class)
|
119
|
+
|
120
|
+
# Try resource param_key first, then fall back to model's natural param key
|
121
|
+
param_key = if params.key?(@resource_class.param_key)
|
122
|
+
@resource_class.param_key
|
123
|
+
elsif params.key?(@resource_class.model_class.model_name.param_key)
|
124
|
+
@resource_class.model_class.model_name.param_key
|
125
|
+
else
|
126
|
+
# Handle namespaced models that use slash format (e.g., "catalog/payment_method")
|
127
|
+
namespaced_param_key = @resource_class.model_class.name.underscore
|
128
|
+
params.key?(namespaced_param_key) ? namespaced_param_key : @resource_class.param_key
|
129
|
+
end
|
130
|
+
|
131
|
+
params.require(param_key).permit(*permitted_attrs)
|
132
|
+
end
|
133
|
+
|
134
|
+
# Determine parameter key for field updates
|
135
|
+
def determine_param_key_for_update_field
|
136
|
+
# Try resource param_key first, then fall back to model's natural param key
|
137
|
+
if params.key?(@resource_class.param_key)
|
138
|
+
@resource_class.param_key
|
139
|
+
elsif params.key?(@resource_class.model_class.model_name.param_key)
|
140
|
+
@resource_class.model_class.model_name.param_key
|
141
|
+
else
|
142
|
+
# Handle namespaced models that use slash format (e.g., "catalog/payment_method")
|
143
|
+
namespaced_param_key = @resource_class.model_class.name.underscore
|
144
|
+
params.key?(namespaced_param_key) ? namespaced_param_key : @resource_class.param_key
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
module EasyAdmin
|
2
|
+
module Concerns
|
3
|
+
# ResourcePagination concern handles pagination and related logic
|
4
|
+
# Provides methods for applying pagination with pagy gem
|
5
|
+
module ResourcePagination
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
# Apply pagination to records using Pagy
|
11
|
+
def apply_pagination(records)
|
12
|
+
# Apply pagination using Pagy with resource-specific configuration
|
13
|
+
items_per_page = @resource_class.respond_to?(:items_per_page) ? @resource_class.items_per_page : 20
|
14
|
+
|
15
|
+
# Reset to page 1 if filtering/searching is applied to avoid overflow
|
16
|
+
current_page = if has_active_filters?
|
17
|
+
1
|
18
|
+
else
|
19
|
+
params[:page] || 1
|
20
|
+
end
|
21
|
+
|
22
|
+
pagination_options = {
|
23
|
+
items: items_per_page,
|
24
|
+
limit: items_per_page,
|
25
|
+
page: current_page,
|
26
|
+
overflow: :last_page # Handle overflow by redirecting to last valid page
|
27
|
+
}
|
28
|
+
|
29
|
+
# Use countless pagination if enabled for performance
|
30
|
+
if @resource_class.respond_to?(:countless_enabled?) && @resource_class.countless_enabled?
|
31
|
+
pagy_countless(records, **pagination_options)
|
32
|
+
else
|
33
|
+
pagy(records, **pagination_options)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Setup sorting parameters with defaults
|
38
|
+
def setup_sorting_params
|
39
|
+
# Set up data needed for both HTML and turbo stream
|
40
|
+
# Default to id DESC if no sort params provided
|
41
|
+
sort_field = params[:sort] || 'id'
|
42
|
+
sort_direction = params[:direction] || (params[:sort] ? 'asc' : 'desc')
|
43
|
+
|
44
|
+
[sort_field, sort_direction]
|
45
|
+
end
|
46
|
+
|
47
|
+
# Set current sorting state for views
|
48
|
+
def set_current_sorting_state(sort_field, sort_direction)
|
49
|
+
@current_sort_field = sort_field
|
50
|
+
@current_sort_direction = sort_direction
|
51
|
+
@current_period = params[:period]
|
52
|
+
end
|
53
|
+
|
54
|
+
# Build Turbo Stream response for infinite scroll
|
55
|
+
def build_infinite_scroll_turbo_stream
|
56
|
+
# For infinite scroll - append new rows to existing table
|
57
|
+
table_rows_html = @records.map do |record|
|
58
|
+
EasyAdmin::Resources::TableRowComponent.new(
|
59
|
+
record: record,
|
60
|
+
resource_class: @resource_class
|
61
|
+
).call
|
62
|
+
end.join.html_safe
|
63
|
+
|
64
|
+
current_params = params.permit(:search, :scope, :sort, :direction, :period, :page, q: {})
|
65
|
+
|
66
|
+
[
|
67
|
+
turbo_stream.append("records-container", table_rows_html),
|
68
|
+
turbo_stream.replace("infinite-scroll-container", EasyAdmin::InfiniteScrollComponent.new(
|
69
|
+
pagy: @pagy,
|
70
|
+
resource_class: @resource_class,
|
71
|
+
current_params: current_params,
|
72
|
+
current_path: request.path
|
73
|
+
).call),
|
74
|
+
turbo_stream.replace("combined-filters", EasyAdmin::CombinedFiltersComponent.new(
|
75
|
+
resource_class: @resource_class,
|
76
|
+
current_params: current_params,
|
77
|
+
search_params: params[:q] || {},
|
78
|
+
current_period: @current_period
|
79
|
+
).call)
|
80
|
+
]
|
81
|
+
end
|
82
|
+
|
83
|
+
# Prepare pagination data for index action
|
84
|
+
def prepare_index_pagination_data
|
85
|
+
sort_field, sort_direction = setup_sorting_params
|
86
|
+
|
87
|
+
# Handle scope filtering
|
88
|
+
@current_scope = determine_current_scope
|
89
|
+
|
90
|
+
# Only calculate scope counts for HTML requests, not for infinite scroll (turbo_stream)
|
91
|
+
if @resource_class.has_scopes? && !request.format.turbo_stream?
|
92
|
+
@scope_counts = calculate_scope_counts
|
93
|
+
end
|
94
|
+
|
95
|
+
# Build filtered records
|
96
|
+
@records = build_filtered_records(sort_field, sort_direction)
|
97
|
+
|
98
|
+
# Apply pagination
|
99
|
+
@pagy, @records = apply_pagination(@records)
|
100
|
+
|
101
|
+
# Set current state for views
|
102
|
+
set_current_sorting_state(sort_field, sort_direction)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Handle pagination-related parameters
|
106
|
+
def pagination_params
|
107
|
+
params.permit(:search, :scope, :sort, :direction, :period, :page, q: {})
|
108
|
+
end
|
109
|
+
|
110
|
+
# Check if we're on first page
|
111
|
+
def first_page?
|
112
|
+
!params[:page].present? || params[:page].to_i <= 1
|
113
|
+
end
|
114
|
+
|
115
|
+
# Check if pagination should reset due to filtering
|
116
|
+
def should_reset_pagination?
|
117
|
+
has_active_filters? && !first_page?
|
118
|
+
end
|
119
|
+
|
120
|
+
# Get pagination info for views
|
121
|
+
def pagination_info
|
122
|
+
return {} unless @pagy
|
123
|
+
|
124
|
+
{
|
125
|
+
current_page: @pagy.page,
|
126
|
+
total_pages: @pagy.pages,
|
127
|
+
total_count: @pagy.count,
|
128
|
+
per_page: @pagy.items,
|
129
|
+
from: @pagy.from,
|
130
|
+
to: @pagy.to
|
131
|
+
}
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -7,16 +7,7 @@ module EasyAdmin
|
|
7
7
|
|
8
8
|
respond_to do |format|
|
9
9
|
format.html
|
10
|
-
format.turbo_stream
|
11
|
-
render turbo_stream: turbo_stream.replace(
|
12
|
-
"dashboard-grid",
|
13
|
-
EasyAdmin::Dashboards::ShowComponent.new(
|
14
|
-
dashboard_class: @dashboard_class,
|
15
|
-
params: dashboard_params,
|
16
|
-
request_path: request.path
|
17
|
-
).call
|
18
|
-
)
|
19
|
-
end
|
10
|
+
format.turbo_stream
|
20
11
|
format.json { render json: dashboard_data }
|
21
12
|
end
|
22
13
|
end
|
@@ -33,42 +24,17 @@ module EasyAdmin
|
|
33
24
|
@card_data = @dashboard_instance.card_data(@card[:name])
|
34
25
|
|
35
26
|
respond_to do |format|
|
36
|
-
format.html
|
37
|
-
|
38
|
-
card: @card,
|
39
|
-
card_data: @card_data,
|
40
|
-
dashboard_class: @dashboard_class
|
41
|
-
).call
|
42
|
-
end
|
43
|
-
format.turbo_stream do
|
44
|
-
render turbo_stream: turbo_stream.replace(
|
45
|
-
"card_#{@card[:name]}",
|
46
|
-
EasyAdmin::Dashboards::CardStreamComponent.new(
|
47
|
-
card: @card,
|
48
|
-
card_data: @card_data,
|
49
|
-
dashboard_class: @dashboard_class
|
50
|
-
).call
|
51
|
-
)
|
52
|
-
end
|
27
|
+
format.html
|
28
|
+
format.turbo_stream
|
53
29
|
format.json { render json: @card_data }
|
54
30
|
end
|
55
31
|
rescue => e
|
56
32
|
Rails.logger.error "Dashboard card error: #{e.message}"
|
33
|
+
@error_message = e.message
|
57
34
|
|
58
35
|
respond_to do |format|
|
59
|
-
format.html
|
60
|
-
|
61
|
-
EasyAdmin::Dashboards::CardErrorComponent.new(error: e.message).call
|
62
|
-
end
|
63
|
-
end
|
64
|
-
format.turbo_stream do
|
65
|
-
render turbo_stream: turbo_stream.replace(
|
66
|
-
"card_#{params[:card_name]}",
|
67
|
-
view_context.turbo_frame_tag("card_#{params[:card_name]}") do
|
68
|
-
EasyAdmin::Dashboards::CardErrorComponent.new(error: e.message).call
|
69
|
-
end
|
70
|
-
)
|
71
|
-
end
|
36
|
+
format.html { render template: 'easy_admin/dashboards/card_error' }
|
37
|
+
format.turbo_stream { render template: 'easy_admin/dashboards/card_error' }
|
72
38
|
format.json { render json: { error: e.message }, status: :server_error }
|
73
39
|
end
|
74
40
|
end
|