rails_pulse 0.2.2 → 0.2.4
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/README.md +1 -1
- data/app/assets/stylesheets/rails_pulse/components/tags.css +2 -2
- data/app/controllers/concerns/chart_table_concern.rb +4 -3
- data/app/controllers/rails_pulse/application_controller.rb +11 -3
- data/app/controllers/rails_pulse/dashboard_controller.rb +12 -8
- data/app/controllers/rails_pulse/queries_controller.rb +13 -8
- data/app/controllers/rails_pulse/requests_controller.rb +10 -5
- data/app/controllers/rails_pulse/routes_controller.rb +14 -7
- data/app/helpers/rails_pulse/application_helper.rb +47 -2
- data/app/helpers/rails_pulse/chart_helper.rb +32 -2
- data/app/javascript/rails_pulse/application.js +3 -54
- data/app/javascript/rails_pulse/controllers/chart_controller.js +229 -0
- data/app/javascript/rails_pulse/controllers/index_controller.js +9 -14
- data/app/javascript/rails_pulse/controllers/pagination_controller.js +27 -33
- data/app/jobs/rails_pulse/backfill_summaries_job.rb +0 -2
- data/app/jobs/rails_pulse/cleanup_job.rb +0 -2
- data/app/jobs/rails_pulse/summary_job.rb +0 -2
- data/app/models/concerns/rails_pulse/taggable.rb +63 -0
- data/app/models/rails_pulse/dashboard/charts/average_response_time.rb +12 -5
- data/app/models/rails_pulse/dashboard/charts/p95_response_time.rb +12 -5
- data/app/models/rails_pulse/dashboard/tables/slow_queries.rb +7 -0
- data/app/models/rails_pulse/dashboard/tables/slow_routes.rb +6 -0
- data/app/models/rails_pulse/queries/cards/average_query_times.rb +10 -6
- data/app/models/rails_pulse/queries/cards/execution_rate.rb +16 -10
- data/app/models/rails_pulse/queries/cards/percentile_query_times.rb +10 -6
- data/app/models/rails_pulse/queries/charts/average_query_times.rb +6 -3
- data/app/models/rails_pulse/queries/tables/index.rb +12 -2
- data/app/models/rails_pulse/requests/charts/average_response_times.rb +13 -7
- data/app/models/rails_pulse/routes/cards/average_response_times.rb +10 -6
- data/app/models/rails_pulse/routes/cards/error_rate_per_route.rb +10 -6
- data/app/models/rails_pulse/routes/cards/percentile_response_times.rb +10 -6
- data/app/models/rails_pulse/routes/cards/request_count_totals.rb +10 -6
- data/app/models/rails_pulse/routes/charts/average_response_times.rb +10 -6
- data/app/models/rails_pulse/routes/tables/index.rb +12 -2
- data/app/models/rails_pulse/summary.rb +55 -0
- data/app/views/layouts/rails_pulse/_global_filters.html.erb +9 -2
- data/app/views/rails_pulse/components/_active_filters.html.erb +36 -0
- data/app/views/rails_pulse/components/_metric_card.html.erb +2 -2
- data/app/views/rails_pulse/components/_page_header.html.erb +4 -0
- data/app/views/rails_pulse/components/_sparkline_stats.html.erb +1 -1
- data/app/views/rails_pulse/components/_table_pagination.html.erb +8 -6
- data/app/views/rails_pulse/csp_test/show.html.erb +1 -1
- data/app/views/rails_pulse/dashboard/charts/_bar_chart.html.erb +1 -1
- data/app/views/rails_pulse/dashboard/index.html.erb +8 -3
- data/app/views/rails_pulse/queries/index.html.erb +3 -2
- data/app/views/rails_pulse/queries/show.html.erb +2 -1
- data/app/views/rails_pulse/requests/index.html.erb +1 -1
- data/app/views/rails_pulse/routes/index.html.erb +3 -2
- data/app/views/rails_pulse/routes/show.html.erb +2 -1
- data/app/views/rails_pulse/tags/_tag_manager.html.erb +2 -2
- data/config/importmap.rb +1 -1
- data/lib/rails_pulse/cleanup_service.rb +8 -0
- data/lib/rails_pulse/engine.rb +0 -5
- data/lib/rails_pulse/version.rb +1 -1
- data/public/rails-pulse-assets/csp-test.js +10 -10
- data/public/rails-pulse-assets/rails-pulse.css +1 -1
- data/public/rails-pulse-assets/rails-pulse.css.map +1 -1
- data/public/rails-pulse-assets/rails-pulse.js +48 -48
- data/public/rails-pulse-assets/rails-pulse.js.map +4 -4
- metadata +7 -26
- data/app/models/concerns/taggable.rb +0 -61
- data/config/initializers/rails_charts_csp_patch.rb +0 -75
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 943ab1e08e81dbe5ede71b7d3c9ca526a65c9a0ce1767c7f9c37cee8883925cf
|
|
4
|
+
data.tar.gz: b3661c728215dc4759e6a3e04dd49dc113d13155972ed30f6ff35d0a2a07b10c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: db5d711b9d8931c6aa489b9069f05d16514a997b9798c743f2c2998611797dbc3dbdcfdcd12e69f65b2b3459292d1e7558075c02805aa0f49a398b85716736ea
|
|
7
|
+
data.tar.gz: 55ccd02b5a9bb13ba46e36e0051c2440d8026956743e02660968209c66e61fe25f4ed8c3dd04a77b08a6209df9e13c922f42e481ade7cf53447d89a93976e680
|
data/README.md
CHANGED
|
@@ -601,7 +601,7 @@ Rails Pulse is built using modern, battle-tested technologies that ensure reliab
|
|
|
601
601
|
- **[Turbo Frames](https://turbo.hotwired.dev/handbook/frames)** - Lazy loading and partial page updates for optimal performance
|
|
602
602
|
|
|
603
603
|
### **Data Visualization**
|
|
604
|
-
- **[
|
|
604
|
+
- **[Apache ECharts](https://echarts.apache.org/)** - Powerful, interactive charting library
|
|
605
605
|
- **[Lucide Icons](https://lucide.dev/)** - Beautiful, consistent iconography with pre-compiled SVG bundle
|
|
606
606
|
|
|
607
607
|
### **Asset Management**
|
|
@@ -2,7 +2,6 @@ module ChartTableConcern
|
|
|
2
2
|
extend ActiveSupport::Concern
|
|
3
3
|
|
|
4
4
|
included do
|
|
5
|
-
include Pagy::Backend
|
|
6
5
|
include TimeRangeConcern
|
|
7
6
|
include ResponseRangeConcern
|
|
8
7
|
include ZoomRangeConcern
|
|
@@ -38,8 +37,10 @@ module ChartTableConcern
|
|
|
38
37
|
start_time: @start_time,
|
|
39
38
|
end_time: @end_time,
|
|
40
39
|
start_duration: @start_duration,
|
|
40
|
+
disabled_tags: session_disabled_tags,
|
|
41
|
+
show_non_tagged: session[:show_non_tagged] != false,
|
|
41
42
|
**chart_options
|
|
42
|
-
).
|
|
43
|
+
).to_chart_data
|
|
43
44
|
end
|
|
44
45
|
|
|
45
46
|
def setup_table_data(ransack_params)
|
|
@@ -50,7 +51,7 @@ module ChartTableConcern
|
|
|
50
51
|
table_results = build_table_results
|
|
51
52
|
handle_pagination
|
|
52
53
|
|
|
53
|
-
@pagy, @table_data = pagy(table_results,
|
|
54
|
+
@pagy, @table_data = pagy(table_results, items: session_pagination_limit)
|
|
54
55
|
end
|
|
55
56
|
|
|
56
57
|
def setup_zoom_range_data
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
module RailsPulse
|
|
2
2
|
class ApplicationController < ActionController::Base
|
|
3
|
+
# Support both Pagy 8.x (Backend) and Pagy 9+ (Method)
|
|
4
|
+
if defined?(Pagy::Method)
|
|
5
|
+
include Pagy::Method
|
|
6
|
+
else
|
|
7
|
+
include Pagy::Backend
|
|
8
|
+
end
|
|
9
|
+
|
|
3
10
|
before_action :authenticate_rails_pulse_user!
|
|
4
11
|
before_action :set_show_non_tagged_default
|
|
5
12
|
helper_method :session_global_filters, :session_disabled_tags
|
|
@@ -8,15 +15,16 @@ module RailsPulse
|
|
|
8
15
|
limit = limit || params[:limit]
|
|
9
16
|
session[:pagination_limit] = limit.to_i if limit.present?
|
|
10
17
|
|
|
11
|
-
|
|
12
|
-
|
|
18
|
+
# Render JSON for direct API calls or AJAX requests (but not turbo frame requests)
|
|
19
|
+
if (request.xhr? && !turbo_frame_request?) || (request.patch? && action_name == "set_pagination_limit")
|
|
13
20
|
render json: { status: "ok" }
|
|
14
|
-
|
|
21
|
+
end
|
|
15
22
|
end
|
|
16
23
|
|
|
17
24
|
def set_global_filters
|
|
18
25
|
if params[:clear] == "true"
|
|
19
26
|
session.delete(:global_filters)
|
|
27
|
+
session[:show_non_tagged] = true # Reset show_non_tagged to default
|
|
20
28
|
else
|
|
21
29
|
filters = session[:global_filters] || {}
|
|
22
30
|
|
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
module RailsPulse
|
|
2
2
|
class DashboardController < ApplicationController
|
|
3
3
|
def index
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
# Get tag filter values from session
|
|
5
|
+
disabled_tags = session_disabled_tags
|
|
6
|
+
show_non_tagged = session[:show_non_tagged] != false
|
|
7
|
+
|
|
8
|
+
@average_query_times_metric_card = RailsPulse::Routes::Cards::AverageResponseTimes.new(route: nil, disabled_tags: disabled_tags, show_non_tagged: show_non_tagged).to_metric_card
|
|
9
|
+
@percentile_response_times_metric_card = RailsPulse::Routes::Cards::PercentileResponseTimes.new(route: nil, disabled_tags: disabled_tags, show_non_tagged: show_non_tagged).to_metric_card
|
|
10
|
+
@request_count_totals_metric_card = RailsPulse::Routes::Cards::RequestCountTotals.new(route: nil, disabled_tags: disabled_tags, show_non_tagged: show_non_tagged).to_metric_card
|
|
11
|
+
@error_rate_per_route_metric_card = RailsPulse::Routes::Cards::ErrorRatePerRoute.new(route: nil, disabled_tags: disabled_tags, show_non_tagged: show_non_tagged).to_metric_card
|
|
8
12
|
|
|
9
13
|
# Generate chart data for inline rendering
|
|
10
|
-
@average_response_time_chart_data = RailsPulse::Dashboard::Charts::AverageResponseTime.new.to_chart_data
|
|
11
|
-
@p95_response_time_chart_data = RailsPulse::Dashboard::Charts::P95ResponseTime.new.to_chart_data
|
|
14
|
+
@average_response_time_chart_data = RailsPulse::Dashboard::Charts::AverageResponseTime.new(disabled_tags: disabled_tags, show_non_tagged: show_non_tagged).to_chart_data
|
|
15
|
+
@p95_response_time_chart_data = RailsPulse::Dashboard::Charts::P95ResponseTime.new(disabled_tags: disabled_tags, show_non_tagged: show_non_tagged).to_chart_data
|
|
12
16
|
|
|
13
17
|
# Generate table data for inline rendering
|
|
14
|
-
@slow_routes_table_data = RailsPulse::Dashboard::Tables::SlowRoutes.new.to_table_data
|
|
15
|
-
@slow_queries_table_data = RailsPulse::Dashboard::Tables::SlowQueries.new.to_table_data
|
|
18
|
+
@slow_routes_table_data = RailsPulse::Dashboard::Tables::SlowRoutes.new(disabled_tags: disabled_tags, show_non_tagged: show_non_tagged).to_table_data
|
|
19
|
+
@slow_queries_table_data = RailsPulse::Dashboard::Tables::SlowQueries.new(disabled_tags: disabled_tags, show_non_tagged: show_non_tagged).to_table_data
|
|
16
20
|
end
|
|
17
21
|
end
|
|
18
22
|
end
|
|
@@ -70,7 +70,8 @@ module RailsPulse
|
|
|
70
70
|
def build_chart_ransack_params(ransack_params)
|
|
71
71
|
base_params = ransack_params.except(:s).merge(
|
|
72
72
|
period_start_gteq: Time.at(@start_time),
|
|
73
|
-
period_start_lt: Time.at(@end_time)
|
|
73
|
+
period_start_lt: Time.at(@end_time),
|
|
74
|
+
summarizable_type_eq: "RailsPulse::Query"
|
|
74
75
|
)
|
|
75
76
|
|
|
76
77
|
# Only add duration filter if we have a meaningful threshold
|
|
@@ -78,8 +79,7 @@ module RailsPulse
|
|
|
78
79
|
|
|
79
80
|
if show_action?
|
|
80
81
|
base_params.merge(
|
|
81
|
-
summarizable_id_eq: @query.id
|
|
82
|
-
summarizable_type_eq: "RailsPulse::Query"
|
|
82
|
+
summarizable_id_eq: @query.id
|
|
83
83
|
)
|
|
84
84
|
else
|
|
85
85
|
base_params
|
|
@@ -123,7 +123,8 @@ module RailsPulse
|
|
|
123
123
|
period_type: period_type,
|
|
124
124
|
start_time: @start_time,
|
|
125
125
|
params: params,
|
|
126
|
-
disabled_tags: session_disabled_tags
|
|
126
|
+
disabled_tags: session_disabled_tags,
|
|
127
|
+
show_non_tagged: session[:show_non_tagged] != false
|
|
127
128
|
).to_table
|
|
128
129
|
end
|
|
129
130
|
end
|
|
@@ -133,9 +134,13 @@ module RailsPulse
|
|
|
133
134
|
def setup_metric_cards
|
|
134
135
|
return if turbo_frame_request?
|
|
135
136
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
137
|
+
# Get tag filter values from session
|
|
138
|
+
disabled_tags = session_disabled_tags
|
|
139
|
+
show_non_tagged = session[:show_non_tagged] != false
|
|
140
|
+
|
|
141
|
+
@average_query_times_metric_card = RailsPulse::Queries::Cards::AverageQueryTimes.new(query: @query, disabled_tags: disabled_tags, show_non_tagged: show_non_tagged).to_metric_card
|
|
142
|
+
@percentile_query_times_metric_card = RailsPulse::Queries::Cards::PercentileQueryTimes.new(query: @query, disabled_tags: disabled_tags, show_non_tagged: show_non_tagged).to_metric_card
|
|
143
|
+
@execution_rate_metric_card = RailsPulse::Queries::Cards::ExecutionRate.new(query: @query, disabled_tags: disabled_tags, show_non_tagged: show_non_tagged).to_metric_card
|
|
139
144
|
end
|
|
140
145
|
|
|
141
146
|
def show_action?
|
|
@@ -158,7 +163,7 @@ module RailsPulse
|
|
|
158
163
|
table_results = build_table_results
|
|
159
164
|
handle_pagination
|
|
160
165
|
|
|
161
|
-
@pagy, @table_data = pagy(table_results,
|
|
166
|
+
@pagy, @table_data = pagy(table_results, items: session_pagination_limit)
|
|
162
167
|
end
|
|
163
168
|
|
|
164
169
|
def handle_pagination
|
|
@@ -26,10 +26,14 @@ module RailsPulse
|
|
|
26
26
|
def setup_metric_cards
|
|
27
27
|
return if turbo_frame_request?
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
# Get tag filter values from session
|
|
30
|
+
disabled_tags = session_disabled_tags
|
|
31
|
+
show_non_tagged = session[:show_non_tagged] != false
|
|
32
|
+
|
|
33
|
+
@average_response_times_metric_card = RailsPulse::Routes::Cards::AverageResponseTimes.new(route: nil, disabled_tags: disabled_tags, show_non_tagged: show_non_tagged).to_metric_card
|
|
34
|
+
@percentile_response_times_metric_card = RailsPulse::Routes::Cards::PercentileResponseTimes.new(route: nil, disabled_tags: disabled_tags, show_non_tagged: show_non_tagged).to_metric_card
|
|
35
|
+
@request_count_totals_metric_card = RailsPulse::Routes::Cards::RequestCountTotals.new(route: nil, disabled_tags: disabled_tags, show_non_tagged: show_non_tagged).to_metric_card
|
|
36
|
+
@error_rate_per_route_metric_card = RailsPulse::Routes::Cards::ErrorRatePerRoute.new(route: nil, disabled_tags: disabled_tags, show_non_tagged: show_non_tagged).to_metric_card
|
|
33
37
|
end
|
|
34
38
|
|
|
35
39
|
|
|
@@ -124,7 +128,8 @@ module RailsPulse
|
|
|
124
128
|
table_results = build_table_results
|
|
125
129
|
handle_pagination
|
|
126
130
|
|
|
127
|
-
|
|
131
|
+
# Use 'items:' for Pagy 8.x compatibility ('limit:' is for Pagy 43+)
|
|
132
|
+
@pagy, @table_data = pagy(table_results, items: session_pagination_limit)
|
|
128
133
|
end
|
|
129
134
|
|
|
130
135
|
def handle_pagination
|
|
@@ -20,10 +20,14 @@ module RailsPulse
|
|
|
20
20
|
def setup_metric_cards
|
|
21
21
|
return if turbo_frame_request?
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
# Get tag filter values from session
|
|
24
|
+
disabled_tags = session_disabled_tags
|
|
25
|
+
show_non_tagged = session[:show_non_tagged] != false
|
|
26
|
+
|
|
27
|
+
@average_query_times_metric_card = RailsPulse::Routes::Cards::AverageResponseTimes.new(route: @route, disabled_tags: disabled_tags, show_non_tagged: show_non_tagged).to_metric_card
|
|
28
|
+
@percentile_response_times_metric_card = RailsPulse::Routes::Cards::PercentileResponseTimes.new(route: @route, disabled_tags: disabled_tags, show_non_tagged: show_non_tagged).to_metric_card
|
|
29
|
+
@request_count_totals_metric_card = RailsPulse::Routes::Cards::RequestCountTotals.new(route: @route, disabled_tags: disabled_tags, show_non_tagged: show_non_tagged).to_metric_card
|
|
30
|
+
@error_rate_per_route_metric_card = RailsPulse::Routes::Cards::ErrorRatePerRoute.new(route: @route, disabled_tags: disabled_tags, show_non_tagged: show_non_tagged).to_metric_card
|
|
27
31
|
end
|
|
28
32
|
|
|
29
33
|
def chart_model
|
|
@@ -87,7 +91,9 @@ module RailsPulse
|
|
|
87
91
|
if show_action?
|
|
88
92
|
# Only show requests that belong to time periods where we have route summaries
|
|
89
93
|
# This ensures the table data is consistent with the chart data
|
|
90
|
-
|
|
94
|
+
# Note: We don't apply tag filters here because we want to show all requests
|
|
95
|
+
# for this specific route, regardless of individual request tags
|
|
96
|
+
base_query = @ransack_query.result
|
|
91
97
|
.joins(<<~SQL)
|
|
92
98
|
INNER JOIN rails_pulse_summaries ON
|
|
93
99
|
rails_pulse_summaries.summarizable_id = rails_pulse_requests.route_id AND
|
|
@@ -110,7 +116,8 @@ module RailsPulse
|
|
|
110
116
|
period_type: period_type,
|
|
111
117
|
start_time: @start_time,
|
|
112
118
|
params: params,
|
|
113
|
-
disabled_tags: session_disabled_tags
|
|
119
|
+
disabled_tags: session_disabled_tags,
|
|
120
|
+
show_non_tagged: session[:show_non_tagged] != false
|
|
114
121
|
).to_table
|
|
115
122
|
end
|
|
116
123
|
end
|
|
@@ -135,7 +142,7 @@ module RailsPulse
|
|
|
135
142
|
table_results = build_table_results
|
|
136
143
|
handle_pagination
|
|
137
144
|
|
|
138
|
-
@pagy, @table_data = pagy(table_results,
|
|
145
|
+
@pagy, @table_data = pagy(table_results, items: session_pagination_limit)
|
|
139
146
|
end
|
|
140
147
|
|
|
141
148
|
def handle_pagination
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
module RailsPulse
|
|
2
2
|
module ApplicationHelper
|
|
3
|
-
include Pagy::Frontend
|
|
4
|
-
|
|
5
3
|
include BreadcrumbsHelper
|
|
6
4
|
include ChartHelper
|
|
7
5
|
include FormattingHelper
|
|
@@ -10,6 +8,10 @@ module RailsPulse
|
|
|
10
8
|
include FormHelper
|
|
11
9
|
include TagsHelper
|
|
12
10
|
|
|
11
|
+
# Include Pagy frontend helpers for Pagy 8.x compatibility
|
|
12
|
+
# Pagy 43+ doesn't need this, but it doesn't hurt to include it
|
|
13
|
+
include Pagy::Frontend if defined?(Pagy::Frontend)
|
|
14
|
+
|
|
13
15
|
# Replacement for lucide_icon helper that works with pre-compiled assets
|
|
14
16
|
# Outputs a custom element that will be hydrated by Stimulus
|
|
15
17
|
def rails_pulse_icon(name, options = {})
|
|
@@ -36,6 +38,49 @@ module RailsPulse
|
|
|
36
38
|
# Backward compatibility alias - can be removed after migration
|
|
37
39
|
alias_method :lucide_icon, :rails_pulse_icon
|
|
38
40
|
|
|
41
|
+
# Get items per page from Pagy instance (compatible with Pagy 8.x and 43+)
|
|
42
|
+
def pagy_items(pagy)
|
|
43
|
+
# Pagy 43+ uses options[:items] or has a limit method
|
|
44
|
+
if pagy.respond_to?(:options) && pagy.options.is_a?(Hash)
|
|
45
|
+
pagy.options[:items]
|
|
46
|
+
# Pagy 8.x uses vars[:items]
|
|
47
|
+
elsif pagy.respond_to?(:vars)
|
|
48
|
+
pagy.vars[:items]
|
|
49
|
+
# Fallback
|
|
50
|
+
else
|
|
51
|
+
pagy.limit || 10
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Get page URL from Pagy instance (compatible with Pagy 8.x and 43+)
|
|
56
|
+
def pagy_page_url(pagy, page_number)
|
|
57
|
+
# Pagy 43+ has page_url method
|
|
58
|
+
if pagy.respond_to?(:page_url)
|
|
59
|
+
pagy.page_url(page_number)
|
|
60
|
+
# Pagy 8.x requires using pagy_url_for helper
|
|
61
|
+
else
|
|
62
|
+
pagy_url_for(pagy, page_number)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Get previous page number (compatible with Pagy 8.x and 43+)
|
|
67
|
+
def pagy_previous(pagy)
|
|
68
|
+
# Pagy 43+ uses 'previous'
|
|
69
|
+
if pagy.respond_to?(:previous)
|
|
70
|
+
pagy.previous
|
|
71
|
+
# Pagy 8.x uses 'prev'
|
|
72
|
+
elsif pagy.respond_to?(:prev)
|
|
73
|
+
pagy.prev
|
|
74
|
+
else
|
|
75
|
+
nil
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Get next page number (compatible with Pagy 8.x and 43+)
|
|
80
|
+
def pagy_next(pagy)
|
|
81
|
+
pagy.respond_to?(:next) ? pagy.next : nil
|
|
82
|
+
end
|
|
83
|
+
|
|
39
84
|
# Make Rails Pulse routes available as rails_pulse in views
|
|
40
85
|
def rails_pulse
|
|
41
86
|
@rails_pulse_helper ||= RailsPulseHelper.new(self)
|
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
module RailsPulse
|
|
2
2
|
module ChartHelper
|
|
3
|
+
# Main chart rendering method - unified API for all chart types
|
|
4
|
+
# Uses Stimulus controller to handle chart initialization
|
|
5
|
+
def render_stimulus_chart(data, type:, **options)
|
|
6
|
+
chart_id = options[:id] || "rails-pulse-chart-#{SecureRandom.hex(8)}"
|
|
7
|
+
height = options[:height] || "400px"
|
|
8
|
+
width = options[:width] || "100%"
|
|
9
|
+
theme = options[:theme] || "railspulse"
|
|
10
|
+
chart_options = options[:options] || {}
|
|
11
|
+
|
|
12
|
+
# Build data attributes for Stimulus
|
|
13
|
+
stimulus_data = {
|
|
14
|
+
controller: "rails-pulse--chart",
|
|
15
|
+
rails_pulse__chart_type_value: type,
|
|
16
|
+
rails_pulse__chart_data_value: data.to_json,
|
|
17
|
+
rails_pulse__chart_options_value: chart_options.to_json,
|
|
18
|
+
rails_pulse__chart_theme_value: theme
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
content_tag(:div, "",
|
|
22
|
+
id: chart_id,
|
|
23
|
+
style: "height: #{height}; width: #{width};",
|
|
24
|
+
data: stimulus_data
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
|
|
3
28
|
# Base chart options shared across all chart types
|
|
4
29
|
def base_chart_options(units: nil, zoom: false)
|
|
5
30
|
{
|
|
@@ -97,16 +122,21 @@ module RailsPulse
|
|
|
97
122
|
|
|
98
123
|
private
|
|
99
124
|
|
|
125
|
+
# Wraps JavaScript function strings for later processing
|
|
126
|
+
def js_function(func_string)
|
|
127
|
+
"__FUNCTION_START__#{func_string}__FUNCTION_END__"
|
|
128
|
+
end
|
|
129
|
+
|
|
100
130
|
def apply_tooltip_formatter(options, tooltip_formatter)
|
|
101
131
|
return unless tooltip_formatter.present?
|
|
102
132
|
|
|
103
|
-
options[:tooltip][:formatter] =
|
|
133
|
+
options[:tooltip][:formatter] = js_function(tooltip_formatter)
|
|
104
134
|
end
|
|
105
135
|
|
|
106
136
|
def apply_xaxis_formatter(options, xaxis_formatter)
|
|
107
137
|
return unless xaxis_formatter.present?
|
|
108
138
|
|
|
109
|
-
options[:xAxis][:axisLabel] ||= { formatter:
|
|
139
|
+
options[:xAxis][:axisLabel] ||= { formatter: js_function(xaxis_formatter) }
|
|
110
140
|
end
|
|
111
141
|
|
|
112
142
|
def apply_zoom_configuration(options, zoom, zoom_start, zoom_end, chart_data)
|
|
@@ -12,6 +12,7 @@ import PopoverController from "./controllers/popover_controller";
|
|
|
12
12
|
import FormController from "./controllers/form_controller";
|
|
13
13
|
|
|
14
14
|
// Rails Pulse Controllers
|
|
15
|
+
import ChartController from "./controllers/chart_controller";
|
|
15
16
|
import IndexController from "./controllers/index_controller";
|
|
16
17
|
import ColorSchemeController from "./controllers/color_scheme_controller";
|
|
17
18
|
import PaginationController from "./controllers/pagination_controller";
|
|
@@ -29,7 +30,7 @@ const application = Application.start();
|
|
|
29
30
|
application.debug = false;
|
|
30
31
|
window.Stimulus = application;
|
|
31
32
|
|
|
32
|
-
// Make ECharts available globally for
|
|
33
|
+
// Make ECharts available globally for chart rendering
|
|
33
34
|
window.echarts = echarts;
|
|
34
35
|
|
|
35
36
|
// Make Turbo available globally
|
|
@@ -42,6 +43,7 @@ application.register("rails-pulse--menu", MenuController);
|
|
|
42
43
|
application.register("rails-pulse--popover", PopoverController);
|
|
43
44
|
application.register("rails-pulse--form", FormController);
|
|
44
45
|
|
|
46
|
+
application.register("rails-pulse--chart", ChartController);
|
|
45
47
|
application.register("rails-pulse--index", IndexController);
|
|
46
48
|
application.register("rails-pulse--color-scheme", ColorSchemeController);
|
|
47
49
|
application.register("rails-pulse--pagination", PaginationController);
|
|
@@ -96,59 +98,6 @@ echarts.registerTheme('railspulse', {
|
|
|
96
98
|
"bar": { "itemStyle": { "barBorderWidth": 0 } }
|
|
97
99
|
});
|
|
98
100
|
|
|
99
|
-
// Chart resize functionality (moved from inline script for CSP compliance)
|
|
100
|
-
window.addEventListener('resize', function() {
|
|
101
|
-
if (window.RailsCharts && window.RailsCharts.charts) {
|
|
102
|
-
Object.keys(window.RailsCharts.charts).forEach(function(chartID) {
|
|
103
|
-
window.RailsCharts.charts[chartID].resize();
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
// Apply axis label colors based on current color scheme
|
|
109
|
-
function applyChartAxisLabelColors() {
|
|
110
|
-
if (!window.RailsCharts || !window.RailsCharts.charts) return;
|
|
111
|
-
const scheme = document.documentElement.getAttribute('data-color-scheme');
|
|
112
|
-
const isDark = scheme === 'dark';
|
|
113
|
-
const axisColor = isDark ? '#ffffff' : '#999999';
|
|
114
|
-
Object.keys(window.RailsCharts.charts).forEach(function(chartID) {
|
|
115
|
-
const chart = window.RailsCharts.charts[chartID];
|
|
116
|
-
try {
|
|
117
|
-
chart.setOption({
|
|
118
|
-
xAxis: { axisLabel: { color: axisColor } },
|
|
119
|
-
yAxis: { axisLabel: { color: axisColor } }
|
|
120
|
-
});
|
|
121
|
-
} catch (e) {
|
|
122
|
-
// noop
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Initial apply after charts initialize and on scheme changes
|
|
128
|
-
document.addEventListener('DOMContentLoaded', () => {
|
|
129
|
-
// run shortly after load to allow charts to initialize
|
|
130
|
-
setTimeout(applyChartAxisLabelColors, 50);
|
|
131
|
-
});
|
|
132
|
-
document.addEventListener('rails-pulse:color-scheme-changed', applyChartAxisLabelColors);
|
|
133
|
-
|
|
134
|
-
// Global function to initialize Rails Charts in any container.
|
|
135
|
-
// This is needed as we render Rails Charts in Turbo Frames.
|
|
136
|
-
window.initializeChartsInContainer = function(containerId) {
|
|
137
|
-
requestAnimationFrame(() => {
|
|
138
|
-
const container = containerId ? document.getElementById(containerId) : document;
|
|
139
|
-
const scripts = container.querySelectorAll('script');
|
|
140
|
-
scripts.forEach(script => {
|
|
141
|
-
const content = script.textContent;
|
|
142
|
-
const match = content.match(/function\s+(init_rails_charts_[a-f0-9]+)/);
|
|
143
|
-
if (match && window[match[1]]) {
|
|
144
|
-
window[match[1]]();
|
|
145
|
-
}
|
|
146
|
-
});
|
|
147
|
-
// ensure colors are correct for any charts initialized in this container
|
|
148
|
-
setTimeout(applyChartAxisLabelColors, 10);
|
|
149
|
-
});
|
|
150
|
-
};
|
|
151
|
-
|
|
152
101
|
// Export for global access
|
|
153
102
|
window.RailsPulse = {
|
|
154
103
|
application,
|