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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/app/assets/stylesheets/rails_pulse/components/tags.css +2 -2
  4. data/app/controllers/concerns/chart_table_concern.rb +4 -3
  5. data/app/controllers/rails_pulse/application_controller.rb +11 -3
  6. data/app/controllers/rails_pulse/dashboard_controller.rb +12 -8
  7. data/app/controllers/rails_pulse/queries_controller.rb +13 -8
  8. data/app/controllers/rails_pulse/requests_controller.rb +10 -5
  9. data/app/controllers/rails_pulse/routes_controller.rb +14 -7
  10. data/app/helpers/rails_pulse/application_helper.rb +47 -2
  11. data/app/helpers/rails_pulse/chart_helper.rb +32 -2
  12. data/app/javascript/rails_pulse/application.js +3 -54
  13. data/app/javascript/rails_pulse/controllers/chart_controller.js +229 -0
  14. data/app/javascript/rails_pulse/controllers/index_controller.js +9 -14
  15. data/app/javascript/rails_pulse/controllers/pagination_controller.js +27 -33
  16. data/app/jobs/rails_pulse/backfill_summaries_job.rb +0 -2
  17. data/app/jobs/rails_pulse/cleanup_job.rb +0 -2
  18. data/app/jobs/rails_pulse/summary_job.rb +0 -2
  19. data/app/models/concerns/rails_pulse/taggable.rb +63 -0
  20. data/app/models/rails_pulse/dashboard/charts/average_response_time.rb +12 -5
  21. data/app/models/rails_pulse/dashboard/charts/p95_response_time.rb +12 -5
  22. data/app/models/rails_pulse/dashboard/tables/slow_queries.rb +7 -0
  23. data/app/models/rails_pulse/dashboard/tables/slow_routes.rb +6 -0
  24. data/app/models/rails_pulse/queries/cards/average_query_times.rb +10 -6
  25. data/app/models/rails_pulse/queries/cards/execution_rate.rb +16 -10
  26. data/app/models/rails_pulse/queries/cards/percentile_query_times.rb +10 -6
  27. data/app/models/rails_pulse/queries/charts/average_query_times.rb +6 -3
  28. data/app/models/rails_pulse/queries/tables/index.rb +12 -2
  29. data/app/models/rails_pulse/requests/charts/average_response_times.rb +13 -7
  30. data/app/models/rails_pulse/routes/cards/average_response_times.rb +10 -6
  31. data/app/models/rails_pulse/routes/cards/error_rate_per_route.rb +10 -6
  32. data/app/models/rails_pulse/routes/cards/percentile_response_times.rb +10 -6
  33. data/app/models/rails_pulse/routes/cards/request_count_totals.rb +10 -6
  34. data/app/models/rails_pulse/routes/charts/average_response_times.rb +10 -6
  35. data/app/models/rails_pulse/routes/tables/index.rb +12 -2
  36. data/app/models/rails_pulse/summary.rb +55 -0
  37. data/app/views/layouts/rails_pulse/_global_filters.html.erb +9 -2
  38. data/app/views/rails_pulse/components/_active_filters.html.erb +36 -0
  39. data/app/views/rails_pulse/components/_metric_card.html.erb +2 -2
  40. data/app/views/rails_pulse/components/_page_header.html.erb +4 -0
  41. data/app/views/rails_pulse/components/_sparkline_stats.html.erb +1 -1
  42. data/app/views/rails_pulse/components/_table_pagination.html.erb +8 -6
  43. data/app/views/rails_pulse/csp_test/show.html.erb +1 -1
  44. data/app/views/rails_pulse/dashboard/charts/_bar_chart.html.erb +1 -1
  45. data/app/views/rails_pulse/dashboard/index.html.erb +8 -3
  46. data/app/views/rails_pulse/queries/index.html.erb +3 -2
  47. data/app/views/rails_pulse/queries/show.html.erb +2 -1
  48. data/app/views/rails_pulse/requests/index.html.erb +1 -1
  49. data/app/views/rails_pulse/routes/index.html.erb +3 -2
  50. data/app/views/rails_pulse/routes/show.html.erb +2 -1
  51. data/app/views/rails_pulse/tags/_tag_manager.html.erb +2 -2
  52. data/config/importmap.rb +1 -1
  53. data/lib/rails_pulse/cleanup_service.rb +8 -0
  54. data/lib/rails_pulse/engine.rb +0 -5
  55. data/lib/rails_pulse/version.rb +1 -1
  56. data/public/rails-pulse-assets/csp-test.js +10 -10
  57. data/public/rails-pulse-assets/rails-pulse.css +1 -1
  58. data/public/rails-pulse-assets/rails-pulse.css.map +1 -1
  59. data/public/rails-pulse-assets/rails-pulse.js +48 -48
  60. data/public/rails-pulse-assets/rails-pulse.js.map +4 -4
  61. metadata +7 -26
  62. data/app/models/concerns/taggable.rb +0 -61
  63. data/config/initializers/rails_charts_csp_patch.rb +0 -75
@@ -2,18 +2,21 @@ module RailsPulse
2
2
  module Queries
3
3
  module Charts
4
4
  class AverageQueryTimes
5
- def initialize(ransack_query:, period_type: nil, query: nil, start_time: nil, end_time: nil, start_duration: nil)
5
+ def initialize(ransack_query:, period_type: nil, query: nil, start_time: nil, end_time: nil, start_duration: nil, disabled_tags: [], show_non_tagged: true)
6
6
  @ransack_query = ransack_query
7
7
  @period_type = period_type
8
8
  @query = query
9
9
  @start_time = start_time
10
10
  @end_time = end_time
11
11
  @start_duration = start_duration
12
+ @disabled_tags = disabled_tags
13
+ @show_non_tagged = show_non_tagged
12
14
  end
13
15
 
14
- def to_rails_chart
15
- # The ransack query already contains the correct filters, just add period_type
16
+ def to_chart_data
17
+ # The ransack query already contains the correct filters, just add period_type and tag filters
16
18
  summaries = @ransack_query.result(distinct: false)
19
+ .with_tag_filters(@disabled_tags, @show_non_tagged)
17
20
  .where(period_type: @period_type)
18
21
  .group(:period_start)
19
22
  .having("AVG(avg_duration) > ?", @start_duration || 0)
@@ -2,13 +2,14 @@ module RailsPulse
2
2
  module Queries
3
3
  module Tables
4
4
  class Index
5
- def initialize(ransack_query:, period_type: nil, start_time:, params:, query: nil, disabled_tags: [])
5
+ def initialize(ransack_query:, period_type: nil, start_time:, params:, query: nil, disabled_tags: [], show_non_tagged: true)
6
6
  @ransack_query = ransack_query
7
7
  @period_type = period_type
8
8
  @start_time = start_time
9
9
  @params = params
10
10
  @query = query
11
11
  @disabled_tags = disabled_tags
12
+ @show_non_tagged = show_non_tagged
12
13
  end
13
14
 
14
15
  def to_table
@@ -23,10 +24,19 @@ module RailsPulse
23
24
  )
24
25
 
25
26
  # Apply tag filters by excluding queries with disabled tags
26
- @disabled_tags.each do |tag|
27
+ # Separate "non_tagged" from actual tags (it's a virtual tag)
28
+ actual_disabled_tags = @disabled_tags.reject { |tag| tag == "non_tagged" }
29
+
30
+ # Exclude queries with actual disabled tags
31
+ actual_disabled_tags.each do |tag|
27
32
  base_query = base_query.where.not("rails_pulse_queries.tags LIKE ?", "%#{tag}%")
28
33
  end
29
34
 
35
+ # Exclude non-tagged queries if show_non_tagged is false
36
+ unless @show_non_tagged
37
+ base_query = base_query.where("rails_pulse_queries.tags IS NOT NULL AND rails_pulse_queries.tags != '[]'")
38
+ end
39
+
30
40
  base_query = base_query.where(summarizable_id: @query.id) if @query
31
41
 
32
42
  # Apply grouping and aggregation
@@ -2,21 +2,27 @@ module RailsPulse
2
2
  module Requests
3
3
  module Charts
4
4
  class AverageResponseTimes
5
- def initialize(ransack_query:, period_type: nil, route: nil, start_time: nil, end_time: nil, start_duration: nil)
5
+ def initialize(ransack_query:, period_type: nil, route: nil, start_time: nil, end_time: nil, start_duration: nil, disabled_tags: [], show_non_tagged: true)
6
6
  @ransack_query = ransack_query
7
7
  @period_type = period_type
8
8
  @route = route
9
9
  @start_time = start_time
10
10
  @end_time = end_time
11
11
  @start_duration = start_duration
12
+ @disabled_tags = disabled_tags
13
+ @show_non_tagged = show_non_tagged
12
14
  end
13
15
 
14
- def to_rails_chart
15
- summaries = @ransack_query.result(distinct: false).where(
16
- summarizable_type: "RailsPulse::Request",
17
- summarizable_id: 0, # Overall request summaries
18
- period_type: @period_type
19
- )
16
+ def to_chart_data
17
+ # Note: Overall request summaries (summarizable_id: 0) are not filtered by tags
18
+ # as they aggregate all requests regardless of route tags
19
+ summaries = @ransack_query.result(distinct: false)
20
+ .with_tag_filters(@disabled_tags, @show_non_tagged)
21
+ .where(
22
+ summarizable_type: "RailsPulse::Request",
23
+ summarizable_id: 0, # Overall request summaries
24
+ period_type: @period_type
25
+ )
20
26
 
21
27
  summaries = summaries
22
28
  .group(:period_start)
@@ -2,8 +2,10 @@ module RailsPulse
2
2
  module Routes
3
3
  module Cards
4
4
  class AverageResponseTimes
5
- def initialize(route:)
5
+ def initialize(route:, disabled_tags: [], show_non_tagged: true)
6
6
  @route = route
7
+ @disabled_tags = disabled_tags
8
+ @show_non_tagged = show_non_tagged
7
9
  end
8
10
 
9
11
  def to_metric_card
@@ -11,11 +13,13 @@ module RailsPulse
11
13
  previous_7_days = 14.days.ago.beginning_of_day
12
14
 
13
15
  # Single query to get all aggregated metrics with conditional sums
14
- base_query = RailsPulse::Summary.where(
15
- summarizable_type: "RailsPulse::Route",
16
- period_type: "day",
17
- period_start: 2.weeks.ago.beginning_of_day..Time.current
18
- )
16
+ base_query = RailsPulse::Summary
17
+ .with_tag_filters(@disabled_tags, @show_non_tagged)
18
+ .where(
19
+ summarizable_type: "RailsPulse::Route",
20
+ period_type: "day",
21
+ period_start: 2.weeks.ago.beginning_of_day..Time.current
22
+ )
19
23
  base_query = base_query.where(summarizable_id: @route.id) if @route
20
24
 
21
25
  metrics = base_query.select(
@@ -2,8 +2,10 @@ module RailsPulse
2
2
  module Routes
3
3
  module Cards
4
4
  class ErrorRatePerRoute
5
- def initialize(route: nil)
5
+ def initialize(route: nil, disabled_tags: [], show_non_tagged: true)
6
6
  @route = route
7
+ @disabled_tags = disabled_tags
8
+ @show_non_tagged = show_non_tagged
7
9
  end
8
10
 
9
11
  def to_metric_card
@@ -11,11 +13,13 @@ module RailsPulse
11
13
  previous_7_days = 14.days.ago.beginning_of_day
12
14
 
13
15
  # Single query to get all error metrics with conditional aggregation
14
- base_query = RailsPulse::Summary.where(
15
- summarizable_type: "RailsPulse::Route",
16
- period_type: "day",
17
- period_start: 2.weeks.ago.beginning_of_day..Time.current
18
- )
16
+ base_query = RailsPulse::Summary
17
+ .with_tag_filters(@disabled_tags, @show_non_tagged)
18
+ .where(
19
+ summarizable_type: "RailsPulse::Route",
20
+ period_type: "day",
21
+ period_start: 2.weeks.ago.beginning_of_day..Time.current
22
+ )
19
23
  base_query = base_query.where(summarizable_id: @route.id) if @route
20
24
 
21
25
  metrics = base_query.select(
@@ -2,8 +2,10 @@ module RailsPulse
2
2
  module Routes
3
3
  module Cards
4
4
  class PercentileResponseTimes
5
- def initialize(route: nil)
5
+ def initialize(route: nil, disabled_tags: [], show_non_tagged: true)
6
6
  @route = route
7
+ @disabled_tags = disabled_tags
8
+ @show_non_tagged = show_non_tagged
7
9
  end
8
10
 
9
11
  def to_metric_card
@@ -11,11 +13,13 @@ module RailsPulse
11
13
  previous_7_days = 14.days.ago.beginning_of_day
12
14
 
13
15
  # Single query to get all P95 metrics with conditional aggregation
14
- base_query = RailsPulse::Summary.where(
15
- summarizable_type: "RailsPulse::Route",
16
- period_type: "day",
17
- period_start: 2.weeks.ago.beginning_of_day..Time.current
18
- )
16
+ base_query = RailsPulse::Summary
17
+ .with_tag_filters(@disabled_tags, @show_non_tagged)
18
+ .where(
19
+ summarizable_type: "RailsPulse::Route",
20
+ period_type: "day",
21
+ period_start: 2.weeks.ago.beginning_of_day..Time.current
22
+ )
19
23
  base_query = base_query.where(summarizable_id: @route.id) if @route
20
24
 
21
25
  metrics = base_query.select(
@@ -2,8 +2,10 @@ module RailsPulse
2
2
  module Routes
3
3
  module Cards
4
4
  class RequestCountTotals
5
- def initialize(route: nil)
5
+ def initialize(route: nil, disabled_tags: [], show_non_tagged: true)
6
6
  @route = route
7
+ @disabled_tags = disabled_tags
8
+ @show_non_tagged = show_non_tagged
7
9
  end
8
10
 
9
11
  def to_metric_card
@@ -11,11 +13,13 @@ module RailsPulse
11
13
  previous_7_days = 14.days.ago.beginning_of_day
12
14
 
13
15
  # Single query to get all count metrics with conditional aggregation
14
- base_query = RailsPulse::Summary.where(
15
- summarizable_type: "RailsPulse::Route",
16
- period_type: "day",
17
- period_start: 2.weeks.ago.beginning_of_day..Time.current
18
- )
16
+ base_query = RailsPulse::Summary
17
+ .with_tag_filters(@disabled_tags, @show_non_tagged)
18
+ .where(
19
+ summarizable_type: "RailsPulse::Route",
20
+ period_type: "day",
21
+ period_start: 2.weeks.ago.beginning_of_day..Time.current
22
+ )
19
23
  base_query = base_query.where(summarizable_id: @route.id) if @route
20
24
 
21
25
  metrics = base_query.select(
@@ -2,20 +2,24 @@ module RailsPulse
2
2
  module Routes
3
3
  module Charts
4
4
  class AverageResponseTimes
5
- def initialize(ransack_query:, period_type: nil, route: nil, start_time: nil, end_time: nil, start_duration: nil)
5
+ def initialize(ransack_query:, period_type: nil, route: nil, start_time: nil, end_time: nil, start_duration: nil, disabled_tags: [], show_non_tagged: true)
6
6
  @ransack_query = ransack_query
7
7
  @period_type = period_type
8
8
  @route = route
9
9
  @start_time = start_time
10
10
  @end_time = end_time
11
11
  @start_duration = start_duration
12
+ @disabled_tags = disabled_tags
13
+ @show_non_tagged = show_non_tagged
12
14
  end
13
15
 
14
- def to_rails_chart
15
- summaries = @ransack_query.result(distinct: false).where(
16
- summarizable_type: "RailsPulse::Route",
17
- period_type: @period_type
18
- )
16
+ def to_chart_data
17
+ summaries = @ransack_query.result(distinct: false)
18
+ .with_tag_filters(@disabled_tags, @show_non_tagged)
19
+ .where(
20
+ summarizable_type: "RailsPulse::Route",
21
+ period_type: @period_type
22
+ )
19
23
 
20
24
  summaries = summaries.where(summarizable_id: @route.id) if @route
21
25
  summaries = summaries
@@ -2,12 +2,13 @@ module RailsPulse
2
2
  module Routes
3
3
  module Tables
4
4
  class Index
5
- def initialize(ransack_query:, period_type: nil, start_time:, params:, disabled_tags: [])
5
+ def initialize(ransack_query:, period_type: nil, start_time:, params:, disabled_tags: [], show_non_tagged: true)
6
6
  @ransack_query = ransack_query
7
7
  @period_type = period_type
8
8
  @start_time = start_time
9
9
  @params = params
10
10
  @disabled_tags = disabled_tags
11
+ @show_non_tagged = show_non_tagged
11
12
  end
12
13
 
13
14
  def to_table
@@ -24,10 +25,19 @@ module RailsPulse
24
25
  )
25
26
 
26
27
  # Apply tag filters by excluding routes with disabled tags
27
- @disabled_tags.each do |tag|
28
+ # Separate "non_tagged" from actual tags (it's a virtual tag)
29
+ actual_disabled_tags = @disabled_tags.reject { |tag| tag == "non_tagged" }
30
+
31
+ # Exclude routes with actual disabled tags
32
+ actual_disabled_tags.each do |tag|
28
33
  base_query = base_query.where.not("rails_pulse_routes.tags LIKE ?", "%#{tag}%")
29
34
  end
30
35
 
36
+ # Exclude non-tagged routes if show_non_tagged is false
37
+ unless @show_non_tagged
38
+ base_query = base_query.where("rails_pulse_routes.tags IS NOT NULL AND rails_pulse_routes.tags != '[]'")
39
+ end
40
+
31
41
  base_query = base_query.where(summarizable_id: @route.id) if @route
32
42
 
33
43
  # Apply grouping and aggregation
@@ -33,6 +33,61 @@ module RailsPulse
33
33
  where(summarizable_type: "RailsPulse::Request", summarizable_id: 0)
34
34
  }
35
35
 
36
+ # Tag filtering scope for charts and metrics
37
+ # Filters summaries based on disabled tags in the underlying route/query
38
+ scope :with_tag_filters, ->(disabled_tags = [], show_non_tagged = true) {
39
+ # Separate "non_tagged" from actual tags (it's a virtual tag)
40
+ actual_disabled_tags = disabled_tags.reject { |tag| tag == "non_tagged" }
41
+
42
+ # Return early if no filters are applied
43
+ return all if actual_disabled_tags.empty? && show_non_tagged
44
+
45
+ # Determine which table to join based on summarizable_type
46
+ # We need to handle both Route and Query summaries
47
+ relation = all
48
+
49
+ # Filter route summaries
50
+ route_ids = RailsPulse::Route.all
51
+
52
+ # Exclude routes with disabled tags
53
+ actual_disabled_tags.each do |tag|
54
+ route_ids = route_ids.where.not("tags LIKE ?", "%#{tag}%")
55
+ end
56
+
57
+ # Exclude non-tagged routes if show_non_tagged is false
58
+ route_ids = route_ids.where("tags IS NOT NULL AND tags != '[]'") unless show_non_tagged
59
+
60
+ route_ids = route_ids.pluck(:id)
61
+
62
+ # Filter query summaries
63
+ query_ids = RailsPulse::Query.all
64
+
65
+ # Exclude queries with disabled tags
66
+ actual_disabled_tags.each do |tag|
67
+ query_ids = query_ids.where.not("tags LIKE ?", "%#{tag}%")
68
+ end
69
+
70
+ # Exclude non-tagged queries if show_non_tagged is false
71
+ query_ids = query_ids.where("tags IS NOT NULL AND tags != '[]'") unless show_non_tagged
72
+
73
+ query_ids = query_ids.pluck(:id)
74
+
75
+ # Apply filters: include only summaries for filtered routes/queries
76
+ # If no routes/queries match the filter, we need to ensure nothing is returned
77
+ # Use -1 as an impossible ID instead of 0 (which might be used for aggregates)
78
+ relation = relation.where(
79
+ "(" \
80
+ " (summarizable_type = 'RailsPulse::Route' AND summarizable_id IN (?)) OR " \
81
+ " (summarizable_type = 'RailsPulse::Query' AND summarizable_id IN (?)) OR " \
82
+ " (summarizable_type = 'RailsPulse::Request')" \
83
+ ")",
84
+ route_ids.presence || [ -1 ],
85
+ query_ids.presence || [ -1 ]
86
+ )
87
+
88
+ relation
89
+ }
90
+
36
91
  # Ransack configuration
37
92
  def self.ransackable_attributes(auth_object = nil)
38
93
  %w[
@@ -15,8 +15,15 @@
15
15
  <div data-rails-pulse--global-filters-target="wrapper" data-action="click->rails-pulse--global-filters#closeOnClickOutside" style="display: none; position: fixed; inset: 0; background-color: rgba(0, 0, 0, 0.8); z-index: 1000; align-items: center; justify-content: center;">
16
16
  <div class="dialog" data-rails-pulse--global-filters-target="dialog" style="position: relative; opacity: 1; transform: scale(1);">
17
17
  <div class="dialog__content">
18
- <h2 class="text-lg font-semibold mb-4">Global Filters</h2>
19
- <p class="text-sm text-subtle mb-4">Set default time filters that persist across all pages. These can be overridden by page-specific filters.</p>
18
+ <div class="flex items-start justify-between">
19
+ <div>
20
+ <h2 class="text-lg font-semibold mbe-4">Global Filters</h2>
21
+ <p class="text-sm text-subtle">Set default time filters that persist across all pages. These can be overridden by page-specific filters.</p>
22
+ </div>
23
+ <%= link_to '#', "aria-label": "Close", data: { action: "rails-pulse--global-filters#close" }, class: "text-subtle hover:text-default" do %>
24
+ <%= rails_pulse_icon 'x', width: '20' %>
25
+ <% end %>
26
+ </div>
20
27
 
21
28
  <%= form_with url: settings_global_filters_path, method: :patch, local: true, data: { action: "submit->rails-pulse--global-filters#submit" } do |form| %>
22
29
  <div class="flex flex-col gap mb-4">
@@ -0,0 +1,36 @@
1
+ <% global_filters = session_global_filters %>
2
+ <% disabled_tags = global_filters['disabled_tags'] || [] %>
3
+ <% has_date_filters = global_filters['start_time'].present? && global_filters['end_time'].present? %>
4
+ <% has_performance_filter = global_filters['performance_threshold'].present? %>
5
+ <% has_tag_filters = disabled_tags.any? || session[:show_non_tagged] == false %>
6
+ <% has_any_filters = has_date_filters || has_performance_filter || has_tag_filters %>
7
+
8
+ <% if has_any_filters %>
9
+ <div class="flex items-center gap text-sm">
10
+ Filtered:
11
+ <% if has_date_filters %>
12
+ <% start_time = Time.parse(global_filters['start_time']) %>
13
+ <% end_time = Time.parse(global_filters['end_time']) %>
14
+ <span class="badge badge--secondary"><%= start_time.strftime("%b %d, %Y %-I:%M %p") %> - <%= end_time.strftime("%b %d, %Y %-I:%M %p") %></span>
15
+ <% end %>
16
+
17
+ <% if has_performance_filter %>
18
+ <% threshold_label = case global_filters['performance_threshold']
19
+ when 'slow' then 'Slow and Above'
20
+ when 'very_slow' then 'Very Slow and Above'
21
+ when 'critical' then 'Critical'
22
+ else global_filters['performance_threshold'].titleize
23
+ end %>
24
+ <span class="badge badge--secondary"><%= threshold_label %></span>
25
+ <% end %>
26
+
27
+ <% if has_tag_filters %>
28
+ <% disabled_tags.each do |tag| %>
29
+ <span class="badge badge--secondary"><%= tag.humanize %></span>
30
+ <% end %>
31
+ <% if session[:show_non_tagged] == false %>
32
+ <span class="badge badge--secondary">Non tagged hidden</span>
33
+ <% end %>
34
+ <% end %>
35
+ </div>
36
+ <% end %>
@@ -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" data-controller="rails-pulse--chart" id="<%= id %>">
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
- <%= bar_chart chart_data, height: "100%", options: chart_options %>
41
+ <%= render_stimulus_chart chart_data, type: 'bar', height: "100%", options: chart_options %>
42
42
  </div>
43
43
  </div>
44
44
  </div>
@@ -16,5 +16,9 @@
16
16
  <div class="breadcrumb-tags">
17
17
  <%= render 'rails_pulse/tags/tag_manager', taggable: taggable %>
18
18
  </div>
19
+ <% elsif defined?(show_active_filters) && show_active_filters %>
20
+ <div class="breadcrumb-tags">
21
+ <%= render 'rails_pulse/components/active_filters' %>
22
+ </div>
19
23
  <% end %>
20
24
  </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
- <%= bar_chart chart_data, height: "100%", options: sparkline_chart_options %>
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.vars[:items]),
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.pages}" %></div>
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
- <%= link_to pagy_url_for(@pagy, 1), class: "btn", aria: { disabled: @pagy.prev.nil? }.compact_blank do %>
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 pagy_url_for(@pagy, @pagy.prev || @pagy.page), class: "btn", aria: { disabled: @pagy.prev.nil? }.compact_blank do %>
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 pagy_url_for(@pagy, @pagy.next || @pagy.page), class: "btn", aria: { disabled: @pagy.next.nil? }.compact_blank do %>
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 pagy_url_for(@pagy, @pagy.last), class: "btn", aria: { disabled: @pagy.next.nil? }.compact_blank do %>
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 rails_charts CSP compliance:</p>
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
- <%= bar_chart @component_data, height: "100%", options: bar_chart_options(units: "ms") %>
1
+ <%= render_stimulus_chart @component_data, type: 'bar', height: "100%", options: bar_chart_options(units: "ms") %>
@@ -1,3 +1,7 @@
1
+ <div class="flex justify-end mb-1">
2
+ <%= render 'rails_pulse/components/active_filters' %>
3
+ </div>
4
+
1
5
  <div class="row">
2
6
  <%= render 'rails_pulse/components/metric_card', { class: "grid-item block", data: @average_query_times_metric_card } %>
3
7
  <%= render 'rails_pulse/components/metric_card', { class: "grid-item block", data: @percentile_response_times_metric_card } %>
@@ -16,9 +20,9 @@
16
20
  } do %>
17
21
  <% if @average_response_time_chart_data.present? %>
18
22
  <div class="chart-container chart-container--slim">
19
- <%= bar_chart(
23
+ <%= render_stimulus_chart(
20
24
  @average_response_time_chart_data,
21
- code: false,
25
+ type: 'bar',
22
26
  id: "dashboard_average_response_time_chart",
23
27
  height: "100%",
24
28
  options: bar_chart_options(
@@ -40,8 +44,9 @@
40
44
  } do %>
41
45
  <% if @p95_response_time_chart_data.present? %>
42
46
  <div class="chart-container chart-container--slim">
43
- <%= bar_chart(
47
+ <%= render_stimulus_chart(
44
48
  @p95_response_time_chart_data,
49
+ type: 'bar',
45
50
  code: false,
46
51
  id: "dashboard_p95_response_time_chart",
47
52
  height: "100%",
@@ -1,4 +1,4 @@
1
- <%= render 'rails_pulse/components/page_header' %>
1
+ <%= render 'rails_pulse/components/page_header', show_active_filters: true %>
2
2
 
3
3
  <% unless turbo_frame_request? %>
4
4
  <div class="row">
@@ -32,8 +32,9 @@
32
32
  class="chart-container chart-container--slim"
33
33
  data-rails-pulse--index-target="chart"
34
34
  >
35
- <%= bar_chart(
35
+ <%= render_stimulus_chart(
36
36
  @chart_data,
37
+ type: 'bar',
37
38
  code: false,
38
39
  id: "average_query_times_chart",
39
40
  height: "100%",
@@ -32,8 +32,9 @@
32
32
  class="chart-container chart-container--slim"
33
33
  data-rails-pulse--index-target="chart"
34
34
  >
35
- <%= bar_chart(
35
+ <%= render_stimulus_chart(
36
36
  @chart_data,
37
+ type: 'bar',
37
38
  code: false,
38
39
  id: "query_responses_chart",
39
40
  height: "100%",
@@ -1,4 +1,4 @@
1
- <%= render 'rails_pulse/components/page_header' %>
1
+ <%= render 'rails_pulse/components/page_header', show_active_filters: true %>
2
2
 
3
3
  <% unless turbo_frame_request? %>
4
4
  <div class="row">
@@ -1,4 +1,4 @@
1
- <%= render 'rails_pulse/components/page_header' %>
1
+ <%= render 'rails_pulse/components/page_header', show_active_filters: true %>
2
2
 
3
3
  <% unless turbo_frame_request? %>
4
4
  <div class="row">
@@ -34,8 +34,9 @@
34
34
  class="chart-container chart-container--slim"
35
35
  data-rails-pulse--index-target="chart"
36
36
  >
37
- <%= bar_chart(
37
+ <%= render_stimulus_chart(
38
38
  @chart_data,
39
+ type: 'bar',
39
40
  code: false,
40
41
  id: "average_response_times_chart",
41
42
  height: "100%",
@@ -35,8 +35,9 @@
35
35
  class="chart-container chart-container--slim"
36
36
  data-rails-pulse--index-target="chart"
37
37
  >
38
- <%= bar_chart(
38
+ <%= render_stimulus_chart(
39
39
  @chart_data,
40
+ type: 'bar',
40
41
  code: false,
41
42
  id: "route_responses_chart",
42
43
  height: "100%",
@@ -11,7 +11,7 @@
11
11
  >
12
12
  <div class="tag-list">
13
13
  <% current_tags.each do |tag| %>
14
- <span class="tag">
14
+ <span class="badge badge--secondary font-normal">
15
15
  <%= tag %>
16
16
  <%= button_to remove_tag_path(taggable_type, taggable.id, tag: tag),
17
17
  method: :delete,
@@ -29,7 +29,7 @@
29
29
  <button
30
30
  type="button"
31
31
  id="tag_menu_button_<%= taggable_type %>_<%= taggable.id %>"
32
- class="btn btn--sm tag-add-button"
32
+ class="badge badge--positive tag-add-button font-normal"
33
33
  data-rails-pulse--popover-target="button"
34
34
  data-action="rails-pulse--popover#toggle"
35
35
  aria-haspopup="true"
data/config/importmap.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  pin "application", to: "rails_pulse/application.js"
2
2
 
3
- # echarts is a dependency of the rails_charts gem
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"