rails_pulse 0.2.3 → 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/controllers/concerns/chart_table_concern.rb +2 -3
- data/app/controllers/rails_pulse/application_controller.rb +10 -3
- data/app/controllers/rails_pulse/queries_controller.rb +1 -1
- data/app/controllers/rails_pulse/requests_controller.rb +2 -1
- data/app/controllers/rails_pulse/routes_controller.rb +1 -1
- 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/rails_pulse/queries/charts/average_query_times.rb +1 -1
- data/app/models/rails_pulse/requests/charts/average_response_times.rb +1 -1
- data/app/models/rails_pulse/routes/charts/average_response_times.rb +1 -1
- data/app/views/rails_pulse/components/_metric_card.html.erb +2 -2
- 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 +4 -3
- data/app/views/rails_pulse/queries/index.html.erb +2 -1
- data/app/views/rails_pulse/queries/show.html.erb +2 -1
- data/app/views/rails_pulse/routes/index.html.erb +2 -1
- data/app/views/rails_pulse/routes/show.html.erb +2 -1
- data/config/importmap.rb +1 -1
- data/lib/rails_pulse/engine.rb +0 -30
- 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.js +48 -48
- data/public/rails-pulse-assets/rails-pulse.js.map +4 -4
- metadata +5 -25
- data/config/initializers/rails_charts_csp_patch.rb +0 -75
|
@@ -3,56 +3,50 @@ import { Controller } from "@hotwired/stimulus"
|
|
|
3
3
|
export default class extends Controller {
|
|
4
4
|
static targets = ["limit"]
|
|
5
5
|
static values = {
|
|
6
|
-
storageKey: { type: String, default: "rails_pulse_pagination_limit" }
|
|
7
|
-
url: String
|
|
6
|
+
storageKey: { type: String, default: "rails_pulse_pagination_limit" }
|
|
8
7
|
}
|
|
9
8
|
|
|
10
9
|
connect() {
|
|
11
10
|
this.restorePaginationLimit()
|
|
12
11
|
}
|
|
13
12
|
|
|
14
|
-
// Update pagination limit and
|
|
13
|
+
// Update pagination limit and navigate to page 1 with new limit
|
|
15
14
|
updateLimit() {
|
|
16
15
|
const limit = this.limitTarget.value
|
|
17
16
|
|
|
18
|
-
// Save to session storage
|
|
17
|
+
// Save to session storage for persistence
|
|
19
18
|
sessionStorage.setItem(this.storageKeyValue, limit)
|
|
20
19
|
|
|
21
|
-
//
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
20
|
+
// Build URL with limit parameter and reset to page 1
|
|
21
|
+
const currentUrl = new URL(window.location)
|
|
22
|
+
currentUrl.searchParams.set('limit', limit)
|
|
23
|
+
currentUrl.searchParams.delete('page')
|
|
24
|
+
|
|
25
|
+
// Use Turbo.visit for smooth navigation that preserves query params
|
|
26
|
+
if (typeof Turbo !== 'undefined') {
|
|
27
|
+
Turbo.visit(currentUrl.toString(), { action: 'replace' })
|
|
28
28
|
} else {
|
|
29
|
-
|
|
30
|
-
const currentUrl = new URL(window.location)
|
|
31
|
-
currentUrl.searchParams.set('limit', limit)
|
|
32
|
-
window.location.href = currentUrl.pathname + currentUrl.search
|
|
29
|
+
window.location.href = currentUrl.toString()
|
|
33
30
|
}
|
|
34
31
|
}
|
|
35
32
|
|
|
36
|
-
//
|
|
37
|
-
getCSRFToken() {
|
|
38
|
-
const token = document.querySelector('meta[name="csrf-token"]')
|
|
39
|
-
return token ? token.getAttribute('content') : ''
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Save the pagination limit to session storage when it changes
|
|
43
|
-
savePaginationLimit() {
|
|
44
|
-
const limit = this.limitTarget.value
|
|
45
|
-
sessionStorage.setItem(this.storageKeyValue, limit)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Restore the pagination limit from session storage on page load
|
|
33
|
+
// Restore the pagination limit from URL or session storage on page load
|
|
49
34
|
restorePaginationLimit() {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
35
|
+
// URL params take precedence over session storage
|
|
36
|
+
const urlParams = new URLSearchParams(window.location.search)
|
|
37
|
+
const urlLimit = urlParams.get('limit')
|
|
38
|
+
|
|
39
|
+
if (urlLimit && this.limitTarget) {
|
|
40
|
+
// Sync sessionStorage with URL param
|
|
41
|
+
sessionStorage.setItem(this.storageKeyValue, urlLimit)
|
|
42
|
+
if (this.limitTarget.value !== urlLimit) {
|
|
43
|
+
this.limitTarget.value = urlLimit
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
// Fall back to sessionStorage if no URL param
|
|
47
|
+
const savedLimit = sessionStorage.getItem(this.storageKeyValue)
|
|
48
|
+
if (savedLimit && this.limitTarget && this.limitTarget.value !== savedLimit) {
|
|
54
49
|
this.limitTarget.value = savedLimit
|
|
55
|
-
// Don't trigger change event when restoring from session - prevents infinite loops
|
|
56
50
|
}
|
|
57
51
|
}
|
|
58
52
|
}
|
|
@@ -13,7 +13,7 @@ module RailsPulse
|
|
|
13
13
|
@show_non_tagged = show_non_tagged
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
def
|
|
16
|
+
def to_chart_data
|
|
17
17
|
# The ransack query already contains the correct filters, just add period_type and tag filters
|
|
18
18
|
summaries = @ransack_query.result(distinct: false)
|
|
19
19
|
.with_tag_filters(@disabled_tags, @show_non_tagged)
|
|
@@ -13,7 +13,7 @@ module RailsPulse
|
|
|
13
13
|
@show_non_tagged = show_non_tagged
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
def
|
|
16
|
+
def to_chart_data
|
|
17
17
|
# Note: Overall request summaries (summarizable_id: 0) are not filtered by tags
|
|
18
18
|
# as they aggregate all requests regardless of route tags
|
|
19
19
|
summaries = @ransack_query.result(distinct: false)
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
trend_amount = data[:trend_amount]
|
|
10
10
|
trend_text = data[:trend_text]
|
|
11
11
|
%>
|
|
12
|
-
<div class="grid-item"
|
|
12
|
+
<div class="grid-item" id="<%= id %>">
|
|
13
13
|
<%= render 'rails_pulse/components/panel', { title: title, card_classes: 'card-compact' } do %>
|
|
14
14
|
<div class="row mbs-2" style="--columns: 2; align-items: center; margin-bottom: 0;">
|
|
15
15
|
<div class="grid-item">
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
}
|
|
39
39
|
)
|
|
40
40
|
%>
|
|
41
|
-
<%=
|
|
41
|
+
<%= render_stimulus_chart chart_data, type: 'bar', height: "100%", options: chart_options %>
|
|
42
42
|
</div>
|
|
43
43
|
</div>
|
|
44
44
|
</div>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<h4 class="text-xl mbs-1 font-bold"><%= summary %></h4>
|
|
3
3
|
</div>
|
|
4
4
|
<div class="chart-container chart-container--slim">
|
|
5
|
-
<%=
|
|
5
|
+
<%= render_stimulus_chart chart_data, type: 'bar', height: "100%", options: sparkline_chart_options %>
|
|
6
6
|
</div>
|
|
7
7
|
<div>
|
|
8
8
|
<span class="badge badge--<%= trend_direction == "down" ? "positive" : "negative" %>-inverse p-0">
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
data-rails-pulse--pagination-url-value="<%= rails_pulse.pagination_limit_path %>">
|
|
8
8
|
<label for="pagination_limit" class="text-sm font-medium">Rows per page</label>
|
|
9
9
|
<%= select_tag :limit,
|
|
10
|
-
options_for_select([[10, 10], [20, 20], [30, 30], [40, 40], [50, 50]], @pagy
|
|
10
|
+
options_for_select([[10, 10], [20, 20], [30, 30], [40, 40], [50, 50]], pagy_items(@pagy)),
|
|
11
11
|
{
|
|
12
12
|
id: "pagination_limit",
|
|
13
13
|
class: "input",
|
|
@@ -21,22 +21,24 @@
|
|
|
21
21
|
%>
|
|
22
22
|
</div>
|
|
23
23
|
|
|
24
|
-
<div class="text-sm font-medium"><%= "Page #{@pagy.page} of #{@pagy.
|
|
24
|
+
<div class="text-sm font-medium"><%= "Page #{@pagy.page} of #{@pagy.last}" %></div>
|
|
25
25
|
|
|
26
26
|
<nav class="flex items-center gap shrink-0" style="--btn-padding: .5rem;" aria-label="Pagination">
|
|
27
|
-
|
|
27
|
+
<% previous_page = pagy_previous(@pagy) %>
|
|
28
|
+
<% next_page = pagy_next(@pagy) %>
|
|
29
|
+
<%= link_to pagy_page_url(@pagy, 1), class: "btn", aria: { disabled: previous_page.nil? }.compact_blank do %>
|
|
28
30
|
<%= rails_pulse_icon 'chevrons-left', width: '16', height: '16' %>
|
|
29
31
|
<span class="sr-only">Go to first page</span>
|
|
30
32
|
<% end %>
|
|
31
|
-
<%= link_to
|
|
33
|
+
<%= link_to pagy_page_url(@pagy, previous_page || @pagy.page), class: "btn", aria: { disabled: previous_page.nil? }.compact_blank do %>
|
|
32
34
|
<%= rails_pulse_icon 'chevron-left', width: '16', height: '16' %>
|
|
33
35
|
<span class="sr-only">Go to previous page</span>
|
|
34
36
|
<% end %>
|
|
35
|
-
<%= link_to
|
|
37
|
+
<%= link_to pagy_page_url(@pagy, next_page || @pagy.page), class: "btn", aria: { disabled: next_page.nil? }.compact_blank do %>
|
|
36
38
|
<%= rails_pulse_icon 'chevron-right', width: '16', height: '16' %>
|
|
37
39
|
<span class="sr-only">Go to next page</span>
|
|
38
40
|
<% end %>
|
|
39
|
-
<%= link_to
|
|
41
|
+
<%= link_to pagy_page_url(@pagy, @pagy.last), class: "btn", aria: { disabled: next_page.nil? }.compact_blank do %>
|
|
40
42
|
<%= rails_pulse_icon 'chevrons-right', width: '16', height: '16' %>
|
|
41
43
|
<span class="sr-only">Go to last page</span>
|
|
42
44
|
<% end %>
|
|
@@ -171,7 +171,7 @@
|
|
|
171
171
|
<div class="card">
|
|
172
172
|
<div class="p-4">
|
|
173
173
|
<h2 class="text-lg font-semibold mb-3">Chart Component Test</h2>
|
|
174
|
-
<p class="text-subtle mb-4">Testing
|
|
174
|
+
<p class="text-subtle mb-4">Testing chart CSP compliance:</p>
|
|
175
175
|
|
|
176
176
|
<div id="chart-container" class="bg-shade border border-main rounded-md p-4 text-center">
|
|
177
177
|
<span class="text-subtle">Chart components would render here in a real dashboard</span>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<%=
|
|
1
|
+
<%= render_stimulus_chart @component_data, type: 'bar', height: "100%", options: bar_chart_options(units: "ms") %>
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
} do %>
|
|
21
21
|
<% if @average_response_time_chart_data.present? %>
|
|
22
22
|
<div class="chart-container chart-container--slim">
|
|
23
|
-
<%=
|
|
23
|
+
<%= render_stimulus_chart(
|
|
24
24
|
@average_response_time_chart_data,
|
|
25
|
-
|
|
25
|
+
type: 'bar',
|
|
26
26
|
id: "dashboard_average_response_time_chart",
|
|
27
27
|
height: "100%",
|
|
28
28
|
options: bar_chart_options(
|
|
@@ -44,8 +44,9 @@
|
|
|
44
44
|
} do %>
|
|
45
45
|
<% if @p95_response_time_chart_data.present? %>
|
|
46
46
|
<div class="chart-container chart-container--slim">
|
|
47
|
-
<%=
|
|
47
|
+
<%= render_stimulus_chart(
|
|
48
48
|
@p95_response_time_chart_data,
|
|
49
|
+
type: 'bar',
|
|
49
50
|
code: false,
|
|
50
51
|
id: "dashboard_p95_response_time_chart",
|
|
51
52
|
height: "100%",
|
data/config/importmap.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
pin "application", to: "rails_pulse/application.js"
|
|
2
2
|
|
|
3
|
-
# echarts is
|
|
3
|
+
# echarts is used for chart rendering
|
|
4
4
|
pin "echarts", to: "echarts.min.js"
|
|
5
5
|
# pin "echarts/theme/inspired", to: "echarts/theme/inspired.js"
|
|
6
6
|
pin "rails_pulse/theme", to: "rails_pulse/theme.js"
|
data/lib/rails_pulse/engine.rb
CHANGED
|
@@ -4,7 +4,6 @@ require "rails_pulse/middleware/asset_server"
|
|
|
4
4
|
require "rails_pulse/subscribers/operation_subscriber"
|
|
5
5
|
require "request_store"
|
|
6
6
|
require "rack/static"
|
|
7
|
-
require "rails_charts"
|
|
8
7
|
require "ransack"
|
|
9
8
|
require "pagy"
|
|
10
9
|
require "turbo-rails"
|
|
@@ -14,17 +13,6 @@ module RailsPulse
|
|
|
14
13
|
class Engine < ::Rails::Engine
|
|
15
14
|
isolate_namespace RailsPulse
|
|
16
15
|
|
|
17
|
-
# Prevent rails_charts from polluting the global ActionView namespace
|
|
18
|
-
# This MUST happen before any initializers run to avoid conflicts with host apps
|
|
19
|
-
# that use Chartkick or other chart libraries
|
|
20
|
-
if defined?(RailsCharts::Engine)
|
|
21
|
-
# Find and remove the rails_charts.helpers initializer
|
|
22
|
-
RailsCharts::Engine.initializers.delete_if do |init|
|
|
23
|
-
init.name == "rails_charts.helpers"
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
|
|
28
16
|
# Load Rake tasks
|
|
29
17
|
rake_tasks do
|
|
30
18
|
Dir.glob(File.expand_path("../tasks/**/*.rake", __FILE__)).each { |file| load file }
|
|
@@ -57,24 +45,6 @@ module RailsPulse
|
|
|
57
45
|
RailsPulse::Subscribers::OperationSubscriber.subscribe!
|
|
58
46
|
end
|
|
59
47
|
|
|
60
|
-
initializer "rails_pulse.rails_charts_theme" do
|
|
61
|
-
RailsCharts.options[:theme] = "railspulse"
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
# Manually include RailsCharts helpers only in RailsPulse views
|
|
65
|
-
# This ensures rails_charts methods are only available in RailsPulse namespace,
|
|
66
|
-
# not in the host application
|
|
67
|
-
initializer "rails_pulse.include_rails_charts_helpers" do
|
|
68
|
-
ActiveSupport.on_load :action_view do
|
|
69
|
-
if defined?(RailsCharts::Helpers) && defined?(RailsPulse::ChartHelper)
|
|
70
|
-
unless RailsPulse::ChartHelper.include?(RailsCharts::Helpers)
|
|
71
|
-
RailsPulse::ChartHelper.include(RailsCharts::Helpers)
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
|
|
78
48
|
initializer "rails_pulse.ransack", after: "ransack.initialize" do
|
|
79
49
|
# Ensure Ransack is loaded before our models
|
|
80
50
|
end
|
data/lib/rails_pulse/version.rb
CHANGED
|
@@ -90,21 +90,21 @@
|
|
|
90
90
|
checkAssetLoading();
|
|
91
91
|
setupAjaxTest();
|
|
92
92
|
trackCSPViolations();
|
|
93
|
+
|
|
94
|
+
// Add a visible indicator for system tests
|
|
95
|
+
const indicator = document.createElement('div');
|
|
96
|
+
indicator.id = 'js-loaded-indicator';
|
|
97
|
+
indicator.textContent = 'CSP Test JS loaded successfully';
|
|
98
|
+
indicator.style.display = 'none'; // Hidden but accessible to tests
|
|
99
|
+
document.body.appendChild(indicator);
|
|
93
100
|
}
|
|
94
|
-
|
|
101
|
+
|
|
102
|
+
console.log('CSP Test JS loaded successfully - monitoring for violations');
|
|
103
|
+
|
|
95
104
|
// Run tests when DOM is ready
|
|
96
105
|
if (document.readyState === 'loading') {
|
|
97
106
|
document.addEventListener('DOMContentLoaded', initializeTests);
|
|
98
107
|
} else {
|
|
99
108
|
initializeTests();
|
|
100
109
|
}
|
|
101
|
-
|
|
102
|
-
console.log('CSP Test JS loaded successfully - monitoring for violations');
|
|
103
|
-
|
|
104
|
-
// Add a visible indicator for system tests
|
|
105
|
-
const indicator = document.createElement('div');
|
|
106
|
-
indicator.id = 'js-loaded-indicator';
|
|
107
|
-
indicator.textContent = 'CSP Test JS loaded successfully';
|
|
108
|
-
indicator.style.display = 'none'; // Hidden but accessible to tests
|
|
109
|
-
document.body.appendChild(indicator);
|
|
110
110
|
})();
|