rails_pulse 0.2.4 → 0.2.5.pre.pre.2

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 (101) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +269 -12
  3. data/Rakefile +142 -8
  4. data/app/assets/stylesheets/rails_pulse/components/table.css +16 -1
  5. data/app/assets/stylesheets/rails_pulse/components/tags.css +7 -2
  6. data/app/assets/stylesheets/rails_pulse/components/utilities.css +3 -0
  7. data/app/controllers/concerns/chart_table_concern.rb +2 -1
  8. data/app/controllers/rails_pulse/application_controller.rb +11 -1
  9. data/app/controllers/rails_pulse/assets_controller.rb +18 -2
  10. data/app/controllers/rails_pulse/job_runs_controller.rb +37 -0
  11. data/app/controllers/rails_pulse/jobs_controller.rb +80 -0
  12. data/app/controllers/rails_pulse/operations_controller.rb +43 -31
  13. data/app/controllers/rails_pulse/queries_controller.rb +1 -1
  14. data/app/controllers/rails_pulse/requests_controller.rb +3 -9
  15. data/app/controllers/rails_pulse/routes_controller.rb +1 -1
  16. data/app/controllers/rails_pulse/tags_controller.rb +31 -5
  17. data/app/helpers/rails_pulse/application_helper.rb +32 -1
  18. data/app/helpers/rails_pulse/breadcrumbs_helper.rb +15 -1
  19. data/app/helpers/rails_pulse/status_helper.rb +16 -0
  20. data/app/helpers/rails_pulse/tags_helper.rb +39 -1
  21. data/app/javascript/rails_pulse/controllers/chart_controller.js +112 -8
  22. data/app/models/concerns/rails_pulse/taggable.rb +25 -2
  23. data/app/models/rails_pulse/charts/operations_chart.rb +33 -0
  24. data/app/models/rails_pulse/dashboard/charts/p95_response_time.rb +1 -2
  25. data/app/models/rails_pulse/dashboard/tables/slow_routes.rb +1 -1
  26. data/app/models/rails_pulse/job.rb +85 -0
  27. data/app/models/rails_pulse/job_run.rb +76 -0
  28. data/app/models/rails_pulse/jobs/cards/average_duration.rb +85 -0
  29. data/app/models/rails_pulse/jobs/cards/base.rb +70 -0
  30. data/app/models/rails_pulse/jobs/cards/failure_rate.rb +85 -0
  31. data/app/models/rails_pulse/jobs/cards/total_jobs.rb +74 -0
  32. data/app/models/rails_pulse/jobs/cards/total_runs.rb +48 -0
  33. data/app/models/rails_pulse/operation.rb +16 -3
  34. data/app/models/rails_pulse/queries/cards/average_query_times.rb +3 -3
  35. data/app/models/rails_pulse/queries/cards/execution_rate.rb +1 -1
  36. data/app/models/rails_pulse/queries/cards/percentile_query_times.rb +1 -1
  37. data/app/models/rails_pulse/queries/tables/index.rb +2 -1
  38. data/app/models/rails_pulse/query.rb +10 -1
  39. data/app/models/rails_pulse/routes/cards/average_response_times.rb +3 -2
  40. data/app/models/rails_pulse/routes/cards/error_rate_per_route.rb +1 -1
  41. data/app/models/rails_pulse/routes/cards/percentile_response_times.rb +1 -1
  42. data/app/models/rails_pulse/routes/cards/request_count_totals.rb +1 -1
  43. data/app/models/rails_pulse/routes/tables/index.rb +2 -1
  44. data/app/models/rails_pulse/summary.rb +10 -3
  45. data/app/services/rails_pulse/summary_service.rb +46 -0
  46. data/app/views/layouts/rails_pulse/_menu_items.html.erb +7 -0
  47. data/app/views/layouts/rails_pulse/application.html.erb +23 -0
  48. data/app/views/rails_pulse/components/_active_filters.html.erb +7 -6
  49. data/app/views/rails_pulse/components/_page_header.html.erb +8 -7
  50. data/app/views/rails_pulse/components/_table.html.erb +7 -4
  51. data/app/views/rails_pulse/dashboard/index.html.erb +1 -1
  52. data/app/views/rails_pulse/job_runs/_operations.html.erb +78 -0
  53. data/app/views/rails_pulse/job_runs/index.html.erb +3 -0
  54. data/app/views/rails_pulse/job_runs/show.html.erb +51 -0
  55. data/app/views/rails_pulse/jobs/_job_runs_table.html.erb +35 -0
  56. data/app/views/rails_pulse/jobs/_table.html.erb +43 -0
  57. data/app/views/rails_pulse/jobs/index.html.erb +34 -0
  58. data/app/views/rails_pulse/jobs/show.html.erb +49 -0
  59. data/app/views/rails_pulse/operations/_operation_analysis_application.html.erb +29 -27
  60. data/app/views/rails_pulse/operations/_operation_analysis_view.html.erb +11 -9
  61. data/app/views/rails_pulse/operations/show.html.erb +10 -8
  62. data/app/views/rails_pulse/queries/_table.html.erb +3 -3
  63. data/app/views/rails_pulse/requests/_table.html.erb +6 -6
  64. data/app/views/rails_pulse/routes/_table.html.erb +3 -3
  65. data/app/views/rails_pulse/routes/show.html.erb +1 -1
  66. data/app/views/rails_pulse/tags/_tag_manager.html.erb +7 -14
  67. data/config/brakeman.ignore +213 -0
  68. data/config/brakeman.yml +68 -0
  69. data/config/initializers/rails_pulse.rb +52 -0
  70. data/config/routes.rb +6 -0
  71. data/db/rails_pulse_migrate/20250113000000_add_jobs_to_rails_pulse.rb +95 -0
  72. data/db/rails_pulse_migrate/20250122000000_add_query_fingerprinting.rb +150 -0
  73. data/db/rails_pulse_migrate/20250202000000_add_index_to_request_uuid.rb +14 -0
  74. data/db/rails_pulse_schema.rb +186 -103
  75. data/lib/generators/rails_pulse/templates/db/rails_pulse_schema.rb +186 -103
  76. data/lib/generators/rails_pulse/templates/migrations/install_rails_pulse_tables.rb +30 -1
  77. data/lib/generators/rails_pulse/templates/rails_pulse.rb +31 -0
  78. data/lib/rails_pulse/active_job_extensions.rb +13 -0
  79. data/lib/rails_pulse/adapters/delayed_job_plugin.rb +25 -0
  80. data/lib/rails_pulse/adapters/sidekiq_middleware.rb +41 -0
  81. data/lib/rails_pulse/cleanup_service.rb +65 -0
  82. data/lib/rails_pulse/configuration.rb +80 -7
  83. data/lib/rails_pulse/engine.rb +34 -3
  84. data/lib/rails_pulse/extensions/active_record.rb +82 -0
  85. data/lib/rails_pulse/job_run_collector.rb +172 -0
  86. data/lib/rails_pulse/middleware/request_collector.rb +20 -43
  87. data/lib/rails_pulse/subscribers/operation_subscriber.rb +11 -5
  88. data/lib/rails_pulse/tracker.rb +82 -0
  89. data/lib/rails_pulse/version.rb +1 -1
  90. data/lib/rails_pulse.rb +2 -0
  91. data/lib/rails_pulse_server.ru +107 -0
  92. data/lib/tasks/rails_pulse_benchmark.rake +382 -0
  93. data/public/rails-pulse-assets/rails-pulse-icons.js +3 -2
  94. data/public/rails-pulse-assets/rails-pulse-icons.js.map +1 -1
  95. data/public/rails-pulse-assets/rails-pulse.css +1 -1
  96. data/public/rails-pulse-assets/rails-pulse.css.map +1 -1
  97. data/public/rails-pulse-assets/rails-pulse.js +1 -1
  98. data/public/rails-pulse-assets/rails-pulse.js.map +3 -3
  99. metadata +35 -7
  100. data/app/models/rails_pulse/requests/charts/operations_chart.rb +0 -35
  101. data/db/migrate/20250930105043_install_rails_pulse_tables.rb +0 -23
@@ -0,0 +1,51 @@
1
+ <%= render 'rails_pulse/components/page_header', taggable: @run %>
2
+
3
+ <div class="row">
4
+ <div class="grid-item">
5
+ <%= render 'rails_pulse/components/panel', title: 'Job Run Details' do %>
6
+ <dl class="descriptive-list mbs-4">
7
+ <dt>Job Class</dt>
8
+ <dd><%= link_to @job.name, job_path(@job) %></dd>
9
+ <dt>Status</dt>
10
+ <dd>
11
+ <%= @run.status.humanize %>
12
+ </dd>
13
+ <dt>Occurred At</dt>
14
+ <dd><%= @run.occurred_at.strftime("%b %d, %Y %l:%M:%S %p") %></dd>
15
+ <% if @run.enqueued_at %>
16
+ <dt>Enqueued At</dt>
17
+ <dd><%= @run.enqueued_at.strftime("%b %d, %Y %l:%M:%S %p") %></dd>
18
+ <% end %>
19
+ <dt>Duration</dt>
20
+ <dd><%= number_to_human(@run.duration || 0, units: { unit: "ms", thousand: "s" }, precision: 2) %></dd>
21
+ <dt>Attempts</dt>
22
+ <dd><%= @run.attempts %></dd>
23
+ <dt>Queue</dt>
24
+ <dd><%= @job.queue_name || 'default' %></dd>
25
+ </dl>
26
+ <% end %>
27
+ </div>
28
+
29
+ <% if @run.error_class.present? %>
30
+ <div class="grid-item">
31
+ <%= render 'rails_pulse/components/panel', title: 'Error Details' do %>
32
+ <dl class="descriptive-list mbs-4">
33
+ <dt>Error Class</dt>
34
+ <dd><%= @run.error_class %></dd>
35
+ <% if @run.error_message.present? %>
36
+ <dt>Error Message</dt>
37
+ <dd><%= @run.error_message %></dd>
38
+ <% end %>
39
+ </dl>
40
+ <% end %>
41
+ </div>
42
+ <% elsif @run.arguments.present? %>
43
+ <div class="grid-item">
44
+ <%= render 'rails_pulse/components/panel', title: 'Arguments' do %>
45
+ <pre class="mbs-4"><%= JSON.pretty_generate(JSON.parse(@run.arguments)) rescue @run.arguments %></pre>
46
+ <% end %>
47
+ </div>
48
+ <% end %>
49
+ </div>
50
+
51
+ <%= render 'operations', operations: @operations %>
@@ -0,0 +1,35 @@
1
+ <% columns = [
2
+ { field: :occurred_at, label: 'Occurred At' },
3
+ { field: :status, label: 'Status', class: 'w-28' },
4
+ { field: :duration, label: 'Duration', class: 'w-28' },
5
+ { field: :attempts, label: 'Attempts', class: 'w-24' },
6
+ { field: nil, label: 'Tags', class: 'w-32', sortable: false }
7
+ ] %>
8
+
9
+ <table class="table mbs-4" data-controller="rails-pulse--table-sort">
10
+ <%= render "rails_pulse/components/table_head", columns: columns %>
11
+
12
+ <tbody>
13
+ <% @table_data.each do |run| %>
14
+ <tr>
15
+ <td>
16
+ <%= link_to run.occurred_at.strftime("%b %d, %Y %l:%M %p"), job_run_path(@job, run), data: { turbo_frame: '_top' } %>
17
+ </td>
18
+ <td>
19
+ <%= run.status.humanize %>
20
+ </td>
21
+ <td>
22
+ <%= number_to_human(run.duration || 0, units: { unit: "ms", thousand: "s" }, precision: 2) %>
23
+ </td>
24
+ <td>
25
+ <%= run.attempts %>
26
+ </td>
27
+ <td>
28
+ <%= display_tag_badges(run) %>
29
+ </td>
30
+ </tr>
31
+ <% end %>
32
+ </tbody>
33
+ </table>
34
+
35
+ <%= render "rails_pulse/components/table_pagination" %>
@@ -0,0 +1,43 @@
1
+ <% columns = [
2
+ { field: :name, label: 'Job Class', class: 'w-48', cell_class: 'truncate-cell' },
3
+ { field: :queue_name, label: 'Queue', class: 'w-32' },
4
+ { field: :runs_count, label: 'Total Runs', class: 'w-28' },
5
+ { field: :failures_count, label: 'Failures', class: 'w-24' },
6
+ { field: :retries_count, label: 'Retries', class: 'w-24' },
7
+ { field: :avg_duration, label: 'Avg Duration', class: 'w-32' },
8
+ { field: nil, label: 'Tags', class: 'w-32', sortable: false }
9
+ ] %>
10
+
11
+ <table class="table table-fixed mbs-4" data-controller="rails-pulse--table-sort">
12
+ <%= render "rails_pulse/components/table_head", columns: columns %>
13
+
14
+ <tbody>
15
+ <% @table_data.each do |job| %>
16
+ <tr>
17
+ <td class="truncate-cell">
18
+ <%= link_to job.name, job_path(job), data: { turbo_frame: '_top' } %>
19
+ </td>
20
+ <td>
21
+ <%= job.queue_name || 'default' %>
22
+ </td>
23
+ <td>
24
+ <%= number_with_delimiter(job.runs_count) %>
25
+ </td>
26
+ <td>
27
+ <%= number_with_delimiter(job.failures_count) %>
28
+ </td>
29
+ <td>
30
+ <%= number_with_delimiter(job.retries_count) %>
31
+ </td>
32
+ <td>
33
+ <%= number_to_human(job.avg_duration || 0, units: { unit: "ms", thousand: "s" }, precision: 2) %>
34
+ </td>
35
+ <td>
36
+ <%= display_tag_badges(job) %>
37
+ </td>
38
+ </tr>
39
+ <% end %>
40
+ </tbody>
41
+ </table>
42
+
43
+ <%= render "rails_pulse/components/table_pagination" %>
@@ -0,0 +1,34 @@
1
+ <%= render 'rails_pulse/components/page_header', show_active_filters: true, show_global_filters: true %>
2
+
3
+ <% unless turbo_frame_request? %>
4
+ <div class="row">
5
+ <%= render 'rails_pulse/components/metric_card', { class: "grid-item block", data: @total_runs_metric_card } %>
6
+ <%= render 'rails_pulse/components/metric_card', { class: "grid-item block", data: @failure_rate_metric_card } %>
7
+ <%= render 'rails_pulse/components/metric_card', { class: "grid-item block", data: @average_duration_metric_card } %>
8
+ </div>
9
+ <% end %>
10
+
11
+ <%= render 'rails_pulse/components/panel', { title: 'Job Classes' } do %>
12
+ <%= search_form_for @ransack_query, url: jobs_path, class: "flex items-center justify-between gap mb-4" do |form| %>
13
+ <div class="flex items-center grow gap">
14
+ <%= form.search_field :name_cont, placeholder: "Filter by job name", autocomplete: "off", class: "input" %>
15
+ <%= form.select :queue_name_eq,
16
+ options_for_select([["All queues", ""]] + @available_queues.map { |q| [q || "default", q] }, params.dig(:q, :queue_name_eq)),
17
+ {},
18
+ { class: "input" }
19
+ %>
20
+ <%= link_to "Reset", jobs_path, class: "btn btn--borderless show@md" if params.has_key?(:q) %>
21
+ <%= form.submit "Search", class: "btn show@sm" %>
22
+ </div>
23
+ <% end %>
24
+
25
+ <% if @jobs.any? %>
26
+ <%= turbo_frame_tag :index_table do %>
27
+ <%= render 'rails_pulse/jobs/table' %>
28
+ <% end %>
29
+ <% else %>
30
+ <%= render 'rails_pulse/components/empty_state',
31
+ title: 'No jobs found',
32
+ description: 'No background jobs have been executed yet.' %>
33
+ <% end %>
34
+ <% end %>
@@ -0,0 +1,49 @@
1
+ <%= render 'rails_pulse/components/page_header', taggable: @job, show_active_filters: true %>
2
+
3
+ <% unless turbo_frame_request? %>
4
+ <div class="row">
5
+ <%= render 'rails_pulse/components/metric_card', { class: "grid-item block", data: @total_runs_metric_card } %>
6
+ <%= render 'rails_pulse/components/metric_card', { class: "grid-item block", data: @failure_rate_metric_card } %>
7
+ <%= render 'rails_pulse/components/metric_card', { class: "grid-item block", data: @average_duration_metric_card } %>
8
+ </div>
9
+ <% end %>
10
+
11
+ <%= render 'rails_pulse/components/panel', { title: 'Job Runs' } do %>
12
+ <%= search_form_for @ransack_query, url: job_path(@job), class: "flex items-center justify-between gap mb-4", data: { controller: "rails-pulse--custom-range" } do |form| %>
13
+ <div class="flex items-center grow gap">
14
+ <%= time_range_selector(form,
15
+ time_range_options: RailsPulse::JobsController::TIME_RANGE_OPTIONS,
16
+ selected_time_range: @selected_time_range,
17
+ mode: :recent_custom
18
+ ) %>
19
+
20
+ <%= form.select :status_eq,
21
+ options_for_select(
22
+ [["All Statuses", ""]] + RailsPulse::JobRun::STATUSES.map { |s| [s.humanize, s] },
23
+ params.dig(:q, :status_eq)
24
+ ),
25
+ {},
26
+ { class: "input" }
27
+ %>
28
+
29
+ <%= form.select :duration_gteq,
30
+ duration_threshold_filter_options(:job),
31
+ { selected: params.dig(:q, :duration_gteq) },
32
+ { class: "input" }
33
+ %>
34
+
35
+ <%= link_to "Reset", job_path(@job), class: "btn btn--borderless show@md" if params.has_key?(:q) %>
36
+ <%= form.submit "Search", class: "btn show@sm" %>
37
+ </div>
38
+ <% end %>
39
+
40
+ <% if @recent_runs.any? %>
41
+ <%= turbo_frame_tag :index_table do %>
42
+ <%= render 'rails_pulse/jobs/job_runs_table' %>
43
+ <% end %>
44
+ <% else %>
45
+ <%= render 'rails_pulse/components/empty_state',
46
+ title: 'No runs found',
47
+ description: 'This job has not been executed yet or no runs match the selected filters.' %>
48
+ <% end %>
49
+ <% end %>
@@ -6,35 +6,37 @@
6
6
  <dd><%= html_escape(action) %></dd>
7
7
  <% end %>
8
8
 
9
- <% total_db_time = operation.request.operations
10
- .where(operation_type: ["sql"])
11
- .sum(:duration) %>
12
- <% total_view_time = operation.request.operations
13
- .where(operation_type: ["template", "partial", "layout", "collection"])
14
- .sum(:duration) %>
15
- <% pure_controller_time = operation.duration - total_db_time - total_view_time %>
9
+ <% if parent %>
10
+ <% total_db_time = parent.operations
11
+ .where(operation_type: ["sql"])
12
+ .sum(:duration) %>
13
+ <% total_view_time = parent.operations
14
+ .where(operation_type: ["template", "partial", "layout", "collection"])
15
+ .sum(:duration) %>
16
+ <% pure_controller_time = operation.duration - total_db_time - total_view_time %>
16
17
 
17
- <% if pure_controller_time > 0 %>
18
- <dt>Pure Logic Time</dt>
19
- <dd>
20
- <%= pure_controller_time.round(1) %>ms
21
- <% if pure_controller_time > 100 %>
22
- - consider optimization
23
- <% end %>
24
- </dd>
25
- <% end %>
18
+ <% if pure_controller_time > 0 %>
19
+ <dt>Pure Logic Time</dt>
20
+ <dd>
21
+ <%= pure_controller_time.round(1) %>ms
22
+ <% if pure_controller_time > 100 %>
23
+ - consider optimization
24
+ <% end %>
25
+ </dd>
26
+ <% end %>
26
27
 
27
- <% db_operations_count = operation.request.operations
28
- .where(operation_type: ["sql"])
29
- .count %>
30
- <% if db_operations_count > 0 %>
31
- <dt>Database Queries</dt>
32
- <dd>
33
- <%= db_operations_count %>
34
- <% if db_operations_count > 10 %>
35
- - potential N+1 queries
36
- <% end %>
37
- </dd>
28
+ <% db_operations_count = parent.operations
29
+ .where(operation_type: ["sql"])
30
+ .count %>
31
+ <% if db_operations_count > 0 %>
32
+ <dt>Database Queries</dt>
33
+ <dd>
34
+ <%= db_operations_count %>
35
+ <% if db_operations_count > 10 %>
36
+ - potential N+1 queries
37
+ <% end %>
38
+ </dd>
39
+ <% end %>
38
40
  <% end %>
39
41
 
40
42
  <% if operation.codebase_location.present? %>
@@ -21,16 +21,18 @@
21
21
  <dd>Partial template - ensure data is pre-loaded in controller</dd>
22
22
  <% end %>
23
23
 
24
- <% view_start = operation.occurred_at %>
25
- <% view_end = operation.occurred_at + operation.duration %>
26
- <% concurrent_db_ops = operation.request.operations
27
- .where(operation_type: ["sql"])
28
- .where("occurred_at >= ? AND occurred_at <= ?", view_start, view_end)
29
- .count %>
24
+ <% if parent %>
25
+ <% view_start = operation.occurred_at %>
26
+ <% view_end = operation.occurred_at + operation.duration %>
27
+ <% concurrent_db_ops = parent.operations
28
+ .where(operation_type: ["sql"])
29
+ .where("occurred_at >= ? AND occurred_at <= ?", view_start, view_end)
30
+ .count %>
30
31
 
31
- <% if concurrent_db_ops > 0 %>
32
- <dt>Queries During Rendering</dt>
33
- <dd><%= concurrent_db_ops %> database <%= 'query'.pluralize(concurrent_db_ops) %></dd>
32
+ <% if concurrent_db_ops > 0 %>
33
+ <dt>Queries During Rendering</dt>
34
+ <dd><%= concurrent_db_ops %> database <%= 'query'.pluralize(concurrent_db_ops) %></dd>
35
+ <% end %>
34
36
  <% end %>
35
37
 
36
38
  <% if operation.duration > 50 %>
@@ -32,19 +32,21 @@
32
32
  <%= @operation.duration.round(2) %> ms
33
33
  </dd>
34
34
 
35
- <dt>Request Impact</dt>
36
- <dd>
37
- <% impact = (@operation.duration / @request.duration * 100).round(1) %>
38
- <%= impact %>% of total request time
39
- </dd>
35
+ <% if @parent %>
36
+ <dt><%= @request ? 'Request' : 'Job Run' %> Impact</dt>
37
+ <dd>
38
+ <% impact = (@operation.duration / @parent.duration * 100).round(1) %>
39
+ <%= impact %>% of total <%= @request ? 'request' : 'job run' %> time
40
+ </dd>
41
+ <% end %>
40
42
 
41
43
  <dt>Occurred At</dt>
42
44
  <dd><%= human_readable_occurred_at @operation.occurred_at %></dd>
43
45
 
44
46
  <% begin %>
45
- <%= render partial: "operation_analysis_#{categorize_operation(@operation.operation_type)}", locals: { operation: @operation } %>
47
+ <%= render partial: "operation_analysis_#{categorize_operation(@operation.operation_type)}", locals: { operation: @operation, parent: @parent } %>
46
48
  <% rescue ActionView::MissingTemplate %>
47
- <%= render partial: "operation_analysis_generic", locals: { operation: @operation } %>
49
+ <%= render partial: "operation_analysis_generic", locals: { operation: @operation, parent: @parent } %>
48
50
  <% end %>
49
51
 
50
52
  <% if @performance_context.any? %>
@@ -59,7 +61,7 @@
59
61
  <% if @related_operations.any? %>
60
62
  <dt>Related Operations</dt>
61
63
  <dd>
62
- <%= pluralize(@related_operations.count, 'similar operation') %> in this request
64
+ <%= pluralize(@related_operations.count, 'similar operation') %> in this <%= @request ? 'request' : 'job run' %>
63
65
  <% if @related_operations.count > 2 %>
64
66
  <br>Potential N+1 query pattern detected
65
67
  <% end %>
@@ -1,17 +1,17 @@
1
1
  <% columns = [
2
- { field: :normalized_sql, label: 'Query', class: 'w-auto' },
2
+ { field: :normalized_sql, label: 'Query', class: 'w-48', cell_class: 'truncate-cell' },
3
3
  { field: :avg_duration_sort, label: 'Average Query Time', class: 'w-44' },
4
4
  { field: :execution_count_sort, label: 'Executions', class: 'w-24' },
5
5
  { field: nil, label: 'Tags', class: 'w-32' }
6
6
  ] %>
7
7
 
8
- <table class="table mbs-4" data-controller="rails-pulse--table-sort">
8
+ <table class="table table-fixed mbs-4" data-controller="rails-pulse--table-sort">
9
9
  <%= render "rails_pulse/components/table_head", columns: columns %>
10
10
 
11
11
  <tbody>
12
12
  <% @table_data.each do |summary| %>
13
13
  <tr>
14
- <td class="whitespace-nowrap">
14
+ <td class="truncate-cell">
15
15
  <div>
16
16
  <%= link_to html_escape(truncate_sql(summary.normalized_sql)), query_path(summary.query_id), data: { turbo_frame: '_top', } %>
17
17
  </div>
@@ -1,12 +1,12 @@
1
1
  <% columns = [
2
- { field: :occurred_at, label: 'Timestamp', class: 'w-36' },
3
- { field: :route_path, label: 'Route', class: 'w-auto' },
2
+ { field: :route_path, label: 'Route', cell_class: 'truncate-cell' },
3
+ { field: :occurred_at, label: 'Occurred At', class: 'w-48' },
4
4
  { field: :duration, label: 'Response Time', class: 'w-36' },
5
5
  { field: :status, label: 'Status', class: 'w-20' },
6
6
  { field: nil, label: 'Tags', class: 'w-32' }
7
7
  ] %>
8
8
 
9
- <table class="table mbs-4" data-controller="rails-pulse--table-sort">
9
+ <table class="table table-fixed mbs-4" data-controller="rails-pulse--table-sort">
10
10
  <%= render "rails_pulse/components/table_head", columns: columns %>
11
11
 
12
12
  <tbody>
@@ -21,11 +21,11 @@
21
21
  end
22
22
  %>
23
23
  <tr>
24
- <td class="whitespace-nowrap">
25
- <%= link_to human_readable_occurred_at(request.occurred_at), request_path(request), data: { turbo_frame: '_top' } %>
24
+ <td class="truncate-cell">
25
+ <%= link_to "#{request.route.path} #{request.route.method}", route_path(request.route), data: { turbo_frame: '_top' } %>
26
26
  </td>
27
27
  <td class="whitespace-nowrap">
28
- <%= link_to "#{request.route.path} #{request.route.method}", route_path(request.route), data: { turbo_frame: '_top' } %>
28
+ <%= link_to human_readable_occurred_at(request.occurred_at), request_path(request), data: { turbo_frame: '_top' } %>
29
29
  </td>
30
30
  <td class="whitespace-nowrap">
31
31
  <span class="<%= performance_class %> font-medium">
@@ -1,18 +1,18 @@
1
1
  <% columns = [
2
- { field: :route_path, label: 'Route', class: 'w-auto' },
2
+ { field: :route_path, label: 'Route', class: 'w-48', cell_class: 'truncate-cell' },
3
3
  { field: :avg_duration_sort, label: 'Average Response Time', class: 'w-48' },
4
4
  { field: :max_duration_sort, label: 'Max Response Time', class: 'w-44' },
5
5
  { field: :count_sort, label: 'Requests', class: 'w-24' },
6
6
  { field: nil, label: 'Tags', class: 'w-32' }
7
7
  ] %>
8
8
 
9
- <table class="table mbs-4" data-controller="rails-pulse--table-sort">
9
+ <table class="table table-fixed mbs-4" data-controller="rails-pulse--table-sort">
10
10
  <%= render "rails_pulse/components/table_head", columns: columns %>
11
11
 
12
12
  <tbody>
13
13
  <% @table_data.each do |summary| %>
14
14
  <tr>
15
- <td class="whitespace-nowrap"><%= link_to "#{summary.path} #{summary.route_method}", route_path(summary.route_id), data: { turbo_frame: '_top' } %></td>
15
+ <td class="truncate-cell"><%= link_to "#{summary.path} #{summary.route_method}", route_path(summary.route_id), data: { turbo_frame: '_top' } %></td>
16
16
  <td class="whitespace-nowrap"><%= summary.avg_duration.to_i %> ms</td>
17
17
  <td class="whitespace-nowrap"><%= summary.max_duration.to_i %> ms</td>
18
18
  <td class="whitespace-nowrap"><%= number_with_delimiter summary.count %></td>
@@ -15,7 +15,7 @@
15
15
  data-rails-pulse--index-chart-id-value="route_responses_chart"
16
16
  >
17
17
  <div class="grid-item">
18
- <%= render 'rails_pulse/components/panel', { title: 'Route Reqeusts', } do %>
18
+ <%= render 'rails_pulse/components/panel', { title: 'Route Requests', } do %>
19
19
  <%= search_form_for @ransack_query, url: route_path(@route), class: "flex items-center justify-between gap mb-4", data: { controller: "rails-pulse--custom-range" } do |form| %>
20
20
  <div class="flex items-center grow gap">
21
21
  <%= time_range_selector(form, time_range_options: RailsPulse::RoutesController::TIME_RANGE_OPTIONS, selected_time_range: @selected_time_range) %>
@@ -7,21 +7,14 @@
7
7
 
8
8
  <div
9
9
  id="tag_manager_<%= taggable_type %>_<%= taggable.id %>"
10
- class="tag-manager"
10
+ class="tag-manager card pi-3 pb-2 text-sm shadow-xs"
11
11
  >
12
+ <% if current_tags.present? %>
13
+ Tags:
14
+ <% end %>
12
15
  <div class="tag-list">
13
16
  <% current_tags.each do |tag| %>
14
- <span class="badge badge--secondary font-normal">
15
- <%= tag %>
16
- <%= button_to remove_tag_path(taggable_type, taggable.id, tag: tag),
17
- method: :delete,
18
- class: "tag-remove",
19
- data: {
20
- turbo_frame: "_top"
21
- } do %>
22
- <span aria-hidden="true">&times;</span>
23
- <% end %>
24
- </span>
17
+ <%= render_tag_badge(tag, variant: :secondary, removable: true, taggable_type: taggable_type, taggable_id: taggable.id) %>
25
18
  <% end %>
26
19
 
27
20
  <% if available_to_add.any? %>
@@ -35,7 +28,7 @@
35
28
  aria-haspopup="true"
36
29
  aria-controls="tag_menu_<%= taggable_type %>_<%= taggable.id %>"
37
30
  >
38
- + tag
31
+ tag +
39
32
  </button>
40
33
 
41
34
  <div
@@ -62,7 +55,7 @@
62
55
  },
63
56
  style: "cursor: pointer;",
64
57
  role: "menuitem" do %>
65
- <%= tag %>
58
+ <%= tag.humanize %>
66
59
  <% end %>
67
60
  <% end %>
68
61
  </div>