solid_queue_monitor 1.2.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +36 -1
  3. data/app/assets/javascripts/solid_queue_monitor/application.js +393 -0
  4. data/app/{services/solid_queue_monitor/stylesheet_generator.rb → assets/stylesheets/solid_queue_monitor/application.css} +80 -12
  5. data/app/controllers/solid_queue_monitor/application_controller.rb +2 -2
  6. data/app/controllers/solid_queue_monitor/assets_controller.rb +52 -0
  7. data/app/controllers/solid_queue_monitor/base_controller.rb +0 -28
  8. data/app/controllers/solid_queue_monitor/failed_jobs_controller.rb +3 -6
  9. data/app/controllers/solid_queue_monitor/in_progress_jobs_controller.rb +3 -6
  10. data/app/controllers/solid_queue_monitor/jobs_controller.rb +3 -6
  11. data/app/controllers/solid_queue_monitor/overview_controller.rb +3 -12
  12. data/app/controllers/solid_queue_monitor/queues_controller.rb +4 -18
  13. data/app/controllers/solid_queue_monitor/ready_jobs_controller.rb +3 -6
  14. data/app/controllers/solid_queue_monitor/recurring_jobs_controller.rb +3 -6
  15. data/app/controllers/solid_queue_monitor/scheduled_jobs_controller.rb +3 -6
  16. data/app/controllers/solid_queue_monitor/search_controller.rb +3 -4
  17. data/app/controllers/solid_queue_monitor/workers_controller.rb +24 -8
  18. data/app/helpers/solid_queue_monitor/application_helper.rb +46 -0
  19. data/app/helpers/solid_queue_monitor/chart_helper.rb +293 -0
  20. data/app/helpers/solid_queue_monitor/job_details_helper.rb +66 -0
  21. data/app/helpers/solid_queue_monitor/jobs_helper.rb +134 -0
  22. data/app/helpers/solid_queue_monitor/pagination_helper.rb +23 -0
  23. data/app/helpers/solid_queue_monitor/sort_helper.rb +30 -0
  24. data/app/helpers/solid_queue_monitor/workers_helper.rb +88 -0
  25. data/app/services/solid_queue_monitor/asset_cache.rb +56 -0
  26. data/app/services/solid_queue_monitor/chart_data_service.rb +2 -2
  27. data/app/views/layouts/solid_queue_monitor/application.html.erb +25 -0
  28. data/app/views/solid_queue_monitor/failed_jobs/_row.html.erb +26 -0
  29. data/app/views/solid_queue_monitor/failed_jobs/index.html.erb +38 -0
  30. data/app/views/solid_queue_monitor/in_progress_jobs/_row.html.erb +13 -0
  31. data/app/views/solid_queue_monitor/in_progress_jobs/index.html.erb +25 -0
  32. data/app/views/solid_queue_monitor/jobs/_arguments.html.erb +9 -0
  33. data/app/views/solid_queue_monitor/jobs/_error.html.erb +26 -0
  34. data/app/views/solid_queue_monitor/jobs/_execution_history.html.erb +25 -0
  35. data/app/views/solid_queue_monitor/jobs/_header.html.erb +37 -0
  36. data/app/views/solid_queue_monitor/jobs/_metadata.html.erb +22 -0
  37. data/app/views/solid_queue_monitor/jobs/_raw_data.html.erb +11 -0
  38. data/app/views/solid_queue_monitor/jobs/_timeline.html.erb +29 -0
  39. data/app/views/solid_queue_monitor/jobs/_timing.html.erb +9 -0
  40. data/app/views/solid_queue_monitor/jobs/_worker.html.erb +12 -0
  41. data/app/views/solid_queue_monitor/jobs/show.html.erb +22 -0
  42. data/app/views/solid_queue_monitor/overview/_chart.html.erb +1 -0
  43. data/app/views/solid_queue_monitor/overview/_recent_job_row.html.erb +26 -0
  44. data/app/views/solid_queue_monitor/overview/_recent_jobs.html.erb +31 -0
  45. data/app/views/solid_queue_monitor/overview/_stat_card.html.erb +4 -0
  46. data/app/views/solid_queue_monitor/overview/_stats.html.erb +11 -0
  47. data/app/views/solid_queue_monitor/overview/index.html.erb +9 -0
  48. data/app/views/solid_queue_monitor/queues/_job_row.html.erb +26 -0
  49. data/app/views/solid_queue_monitor/queues/_row.html.erb +33 -0
  50. data/app/views/solid_queue_monitor/queues/index.html.erb +18 -0
  51. data/app/views/solid_queue_monitor/queues/show.html.erb +63 -0
  52. data/app/views/solid_queue_monitor/ready_jobs/_row.html.erb +7 -0
  53. data/app/views/solid_queue_monitor/ready_jobs/index.html.erb +25 -0
  54. data/app/views/solid_queue_monitor/recurring_jobs/_row.html.erb +8 -0
  55. data/app/views/solid_queue_monitor/recurring_jobs/index.html.erb +26 -0
  56. data/app/views/solid_queue_monitor/scheduled_jobs/_row.html.erb +7 -0
  57. data/app/views/solid_queue_monitor/scheduled_jobs/index.html.erb +36 -0
  58. data/app/views/solid_queue_monitor/search/_completed_row.html.erb +6 -0
  59. data/app/views/solid_queue_monitor/search/_failed_row.html.erb +6 -0
  60. data/app/views/solid_queue_monitor/search/_job_row.html.erb +9 -0
  61. data/app/views/solid_queue_monitor/search/_recurring_row.html.erb +6 -0
  62. data/app/views/solid_queue_monitor/search/_section.html.erb +25 -0
  63. data/app/views/solid_queue_monitor/search/index.html.erb +23 -0
  64. data/app/views/solid_queue_monitor/shared/_filters.html.erb +48 -0
  65. data/app/views/solid_queue_monitor/shared/_flash.html.erb +17 -0
  66. data/app/views/solid_queue_monitor/shared/_footer.html.erb +3 -0
  67. data/app/views/solid_queue_monitor/shared/_header.html.erb +81 -0
  68. data/app/views/solid_queue_monitor/shared/_jobs_table.html.erb +20 -0
  69. data/app/views/solid_queue_monitor/shared/_pagination.html.erb +25 -0
  70. data/app/views/solid_queue_monitor/workers/_row.html.erb +22 -0
  71. data/app/views/solid_queue_monitor/workers/index.html.erb +82 -0
  72. data/config/routes.rb +6 -1
  73. data/lib/solid_queue_monitor/engine.rb +2 -0
  74. data/lib/solid_queue_monitor/version.rb +1 -1
  75. metadata +57 -17
  76. data/app/presenters/solid_queue_monitor/base_presenter.rb +0 -211
  77. data/app/presenters/solid_queue_monitor/failed_jobs_presenter.rb +0 -312
  78. data/app/presenters/solid_queue_monitor/in_progress_jobs_presenter.rb +0 -84
  79. data/app/presenters/solid_queue_monitor/job_details_presenter.rb +0 -696
  80. data/app/presenters/solid_queue_monitor/jobs_presenter.rb +0 -144
  81. data/app/presenters/solid_queue_monitor/queue_details_presenter.rb +0 -195
  82. data/app/presenters/solid_queue_monitor/queues_presenter.rb +0 -89
  83. data/app/presenters/solid_queue_monitor/ready_jobs_presenter.rb +0 -81
  84. data/app/presenters/solid_queue_monitor/recurring_jobs_presenter.rb +0 -81
  85. data/app/presenters/solid_queue_monitor/scheduled_jobs_presenter.rb +0 -173
  86. data/app/presenters/solid_queue_monitor/search_results_presenter.rb +0 -190
  87. data/app/presenters/solid_queue_monitor/stats_presenter.rb +0 -36
  88. data/app/presenters/solid_queue_monitor/workers_presenter.rb +0 -320
  89. data/app/services/solid_queue_monitor/chart_presenter.rb +0 -239
  90. data/app/services/solid_queue_monitor/html_generator.rb +0 -401
@@ -0,0 +1,20 @@
1
+ <div class="table-container">
2
+ <table>
3
+ <thead>
4
+ <tr>
5
+ <% columns.each do |column| %>
6
+ <% if column[:sort_key] %>
7
+ <%= sortable_header(column[:sort_key], column[:label], sort: sort, filters: filters) %>
8
+ <% else %>
9
+ <th><%= column[:label] %></th>
10
+ <% end %>
11
+ <% end %>
12
+ </tr>
13
+ </thead>
14
+ <tbody>
15
+ <% jobs.each do |job| %>
16
+ <%= render row_partial, job: job %>
17
+ <% end %>
18
+ </tbody>
19
+ </table>
20
+ </div>
@@ -0,0 +1,25 @@
1
+ <% if total_pages > 1 %>
2
+ <div class="pagination">
3
+ <% if current_page > 1 %>
4
+ <%= link_to 'Previous', pagination_href(current_page - 1), class: 'pagination-link pagination-nav' %>
5
+ <% else %>
6
+ <span class="pagination-link pagination-nav disabled">Previous</span>
7
+ <% end %>
8
+
9
+ <% visible_pages(current_page, total_pages).each do |page| %>
10
+ <% if page == :gap %>
11
+ <span class="pagination-gap">...</span>
12
+ <% elsif page == current_page %>
13
+ <span class="pagination-current"><%= page %></span>
14
+ <% else %>
15
+ <%= link_to page, pagination_href(page), class: 'pagination-link' %>
16
+ <% end %>
17
+ <% end %>
18
+
19
+ <% if current_page < total_pages %>
20
+ <%= link_to 'Next', pagination_href(current_page + 1), class: 'pagination-link pagination-nav' %>
21
+ <% else %>
22
+ <span class="pagination-link pagination-nav disabled">Next</span>
23
+ <% end %>
24
+ </div>
25
+ <% end %>
@@ -0,0 +1,22 @@
1
+ <% status = worker_status(job) %>
2
+ <tr class="<%= worker_row_class(job) %>">
3
+ <td><%= worker_kind_badge(job.kind) %></td>
4
+ <td><%= worker_hostname(job) %></td>
5
+ <td><code><%= job.pid %></code></td>
6
+ <td><%= worker_queues(job) %></td>
7
+ <td><%= worker_heartbeat(job.last_heartbeat_at) %></td>
8
+ <td><%= worker_status_badge(status) %></td>
9
+ <td><%= worker_jobs_processing(job, claimed_counts: @claimed_counts, claimed_jobs: @claimed_jobs) %></td>
10
+ <td class="actions-cell">
11
+ <% if status == :dead %>
12
+ <form action="<%= remove_worker_path(id: job.id) %>"
13
+ method="post"
14
+ class="inline-form"
15
+ data-confirm="Remove this dead process from the registry?">
16
+ <button type="submit" class="action-button discard-button" title="Remove dead process">Remove</button>
17
+ </form>
18
+ <% else %>
19
+ <span class="action-placeholder">-</span>
20
+ <% end %>
21
+ </td>
22
+ </tr>
@@ -0,0 +1,82 @@
1
+ <% content_for :title, 'Workers' %>
2
+
3
+ <div class="workers-summary">
4
+ <div class="summary-card">
5
+ <span class="summary-label">Total Processes</span>
6
+ <span class="summary-value"><%= @summary[:total] %></span>
7
+ </div>
8
+ <div class="summary-card summary-healthy">
9
+ <span class="summary-label">Healthy</span>
10
+ <span class="summary-value"><%= @summary[:healthy] %></span>
11
+ </div>
12
+ <div class="summary-card summary-stale">
13
+ <span class="summary-label">Stale</span>
14
+ <span class="summary-value"><%= @summary[:stale] %></span>
15
+ </div>
16
+ <div class="summary-card summary-dead">
17
+ <span class="summary-label">Dead</span>
18
+ <span class="summary-value"><%= @summary[:dead] %></span>
19
+ <% if @summary[:dead].positive? %>
20
+ <% suffix = @summary[:dead] > 1 ? 'es' : '' %>
21
+ <a href="#"
22
+ class="summary-action"
23
+ data-confirm-submit="prune-all-form"
24
+ data-confirm="Remove all <%= @summary[:dead] %> dead process<%= suffix %>? This will clean up processes that have stopped sending heartbeats.">
25
+ Prune all
26
+ </a>
27
+ <form id="prune-all-form" action="<%= prune_workers_path %>" method="post" class="is-hidden"></form>
28
+ <% end %>
29
+ </div>
30
+ </div>
31
+
32
+ <div class="filter-form-container">
33
+ <%= form_with url: workers_path, method: :get, local: true, html: { class: 'filter-form' } do |form| %>
34
+ <div class="filter-group">
35
+ <%= form.label :kind, 'Kind:' %>
36
+ <%= form.select :kind,
37
+ options_for_select([['All', ''], %w[Worker Worker], %w[Dispatcher Dispatcher], %w[Scheduler Scheduler]], @filters[:kind]) %>
38
+ </div>
39
+ <div class="filter-group">
40
+ <%= form.label :hostname, 'Hostname:' %>
41
+ <%= form.text_field :hostname, value: @filters[:hostname], placeholder: 'Filter by hostname' %>
42
+ </div>
43
+ <div class="filter-group">
44
+ <%= form.label :status, 'Status:' %>
45
+ <%= form.select :status,
46
+ options_for_select([['All', ''], %w[Healthy healthy], %w[Stale stale], %w[Dead dead]], @filters[:status]) %>
47
+ </div>
48
+ <div class="filter-actions">
49
+ <%= form.submit 'Apply Filters', class: 'filter-button' %>
50
+ <%= link_to 'Reset', workers_path, class: 'reset-button' %>
51
+ </div>
52
+ <% end %>
53
+ </div>
54
+
55
+ <% if @process_records.empty? %>
56
+ <div class="empty-state">
57
+ <p>No worker processes found.</p>
58
+ <p class="empty-state-hint">Workers will appear here when Solid Queue processes are running.</p>
59
+ </div>
60
+ <% else %>
61
+ <% columns = [
62
+ { sort_key: nil, label: 'Kind' },
63
+ { sort_key: :hostname, label: 'Hostname' },
64
+ { sort_key: nil, label: 'PID' },
65
+ { sort_key: nil, label: 'Queues' },
66
+ { sort_key: :last_heartbeat_at, label: 'Last Heartbeat' },
67
+ { sort_key: nil, label: 'Status' },
68
+ { sort_key: nil, label: 'Jobs Processing' },
69
+ { sort_key: nil, label: 'Actions' }
70
+ ] %>
71
+
72
+ <%= render 'solid_queue_monitor/shared/jobs_table',
73
+ jobs: @process_records,
74
+ columns: columns,
75
+ sort: @sort,
76
+ filters: @filters,
77
+ row_partial: 'solid_queue_monitor/workers/row' %>
78
+ <% end %>
79
+
80
+ <%= render 'solid_queue_monitor/shared/pagination',
81
+ current_page: @processes[:current_page],
82
+ total_pages: @processes[:total_pages] %>
data/config/routes.rb CHANGED
@@ -15,7 +15,7 @@ SolidQueueMonitor::Engine.routes.draw do
15
15
  resources :failed_jobs, only: [:index]
16
16
  resources :in_progress_jobs, only: [:index]
17
17
  resources :queues, only: [:index]
18
- get 'queues/:queue_name', to: 'queues#show', as: :queue_details, constraints: { queue_name: /[^\/]+/ }
18
+ get 'queues/:queue_name', to: 'queues#show', as: :queue_details, constraints: { queue_name: %r{[^/]+} }
19
19
  resources :workers, only: [:index]
20
20
  resources :jobs, only: [:show]
21
21
 
@@ -33,4 +33,9 @@ SolidQueueMonitor::Engine.routes.draw do
33
33
 
34
34
  post 'remove_worker/:id', to: 'workers#remove', as: :remove_worker
35
35
  post 'prune_workers', to: 'workers#prune', as: :prune_workers
36
+
37
+ get 'assets/:file',
38
+ to: 'assets#show',
39
+ as: :asset,
40
+ constraints: { file: /[A-Za-z0-9_\-]+-[a-f0-9]+\.(css|js)/ }
36
41
  end
@@ -5,9 +5,11 @@ module SolidQueueMonitor
5
5
  isolate_namespace SolidQueueMonitor
6
6
 
7
7
  config.autoload_paths << root.join('app', 'services')
8
+ config.autoload_paths << root.join('app', 'helpers')
8
9
 
9
10
  # Optional: Add eager loading for production
10
11
  config.eager_load_paths << root.join('app', 'services')
12
+ config.eager_load_paths << root.join('app', 'helpers')
11
13
 
12
14
  # Ensure session middleware is available
13
15
  initializer 'solid_queue_monitor.middleware' do |app|
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SolidQueueMonitor
4
- VERSION = '1.2.2'
4
+ VERSION = '2.0.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solid_queue_monitor
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.2
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vishal Sadriya
@@ -48,7 +48,10 @@ files:
48
48
  - LICENSE
49
49
  - README.md
50
50
  - Rakefile
51
+ - app/assets/javascripts/solid_queue_monitor/application.js
52
+ - app/assets/stylesheets/solid_queue_monitor/application.css
51
53
  - app/controllers/solid_queue_monitor/application_controller.rb
54
+ - app/controllers/solid_queue_monitor/assets_controller.rb
52
55
  - app/controllers/solid_queue_monitor/base_controller.rb
53
56
  - app/controllers/solid_queue_monitor/failed_jobs_controller.rb
54
57
  - app/controllers/solid_queue_monitor/in_progress_jobs_controller.rb
@@ -60,32 +63,69 @@ files:
60
63
  - app/controllers/solid_queue_monitor/scheduled_jobs_controller.rb
61
64
  - app/controllers/solid_queue_monitor/search_controller.rb
62
65
  - app/controllers/solid_queue_monitor/workers_controller.rb
63
- - app/presenters/solid_queue_monitor/base_presenter.rb
64
- - app/presenters/solid_queue_monitor/failed_jobs_presenter.rb
65
- - app/presenters/solid_queue_monitor/in_progress_jobs_presenter.rb
66
- - app/presenters/solid_queue_monitor/job_details_presenter.rb
67
- - app/presenters/solid_queue_monitor/jobs_presenter.rb
68
- - app/presenters/solid_queue_monitor/queue_details_presenter.rb
69
- - app/presenters/solid_queue_monitor/queues_presenter.rb
70
- - app/presenters/solid_queue_monitor/ready_jobs_presenter.rb
71
- - app/presenters/solid_queue_monitor/recurring_jobs_presenter.rb
72
- - app/presenters/solid_queue_monitor/scheduled_jobs_presenter.rb
73
- - app/presenters/solid_queue_monitor/search_results_presenter.rb
74
- - app/presenters/solid_queue_monitor/stats_presenter.rb
75
- - app/presenters/solid_queue_monitor/workers_presenter.rb
66
+ - app/helpers/solid_queue_monitor/application_helper.rb
67
+ - app/helpers/solid_queue_monitor/chart_helper.rb
68
+ - app/helpers/solid_queue_monitor/job_details_helper.rb
69
+ - app/helpers/solid_queue_monitor/jobs_helper.rb
70
+ - app/helpers/solid_queue_monitor/pagination_helper.rb
71
+ - app/helpers/solid_queue_monitor/sort_helper.rb
72
+ - app/helpers/solid_queue_monitor/workers_helper.rb
73
+ - app/services/solid_queue_monitor/asset_cache.rb
76
74
  - app/services/solid_queue_monitor/authentication_service.rb
77
75
  - app/services/solid_queue_monitor/chart_data_service.rb
78
- - app/services/solid_queue_monitor/chart_presenter.rb
79
76
  - app/services/solid_queue_monitor/execute_job_service.rb
80
77
  - app/services/solid_queue_monitor/failed_job_service.rb
81
- - app/services/solid_queue_monitor/html_generator.rb
82
78
  - app/services/solid_queue_monitor/pagination_service.rb
83
79
  - app/services/solid_queue_monitor/queue_pause_service.rb
84
80
  - app/services/solid_queue_monitor/reject_job_service.rb
85
81
  - app/services/solid_queue_monitor/search_service.rb
86
82
  - app/services/solid_queue_monitor/stats_calculator.rb
87
83
  - app/services/solid_queue_monitor/status_calculator.rb
88
- - app/services/solid_queue_monitor/stylesheet_generator.rb
84
+ - app/views/layouts/solid_queue_monitor/application.html.erb
85
+ - app/views/solid_queue_monitor/failed_jobs/_row.html.erb
86
+ - app/views/solid_queue_monitor/failed_jobs/index.html.erb
87
+ - app/views/solid_queue_monitor/in_progress_jobs/_row.html.erb
88
+ - app/views/solid_queue_monitor/in_progress_jobs/index.html.erb
89
+ - app/views/solid_queue_monitor/jobs/_arguments.html.erb
90
+ - app/views/solid_queue_monitor/jobs/_error.html.erb
91
+ - app/views/solid_queue_monitor/jobs/_execution_history.html.erb
92
+ - app/views/solid_queue_monitor/jobs/_header.html.erb
93
+ - app/views/solid_queue_monitor/jobs/_metadata.html.erb
94
+ - app/views/solid_queue_monitor/jobs/_raw_data.html.erb
95
+ - app/views/solid_queue_monitor/jobs/_timeline.html.erb
96
+ - app/views/solid_queue_monitor/jobs/_timing.html.erb
97
+ - app/views/solid_queue_monitor/jobs/_worker.html.erb
98
+ - app/views/solid_queue_monitor/jobs/show.html.erb
99
+ - app/views/solid_queue_monitor/overview/_chart.html.erb
100
+ - app/views/solid_queue_monitor/overview/_recent_job_row.html.erb
101
+ - app/views/solid_queue_monitor/overview/_recent_jobs.html.erb
102
+ - app/views/solid_queue_monitor/overview/_stat_card.html.erb
103
+ - app/views/solid_queue_monitor/overview/_stats.html.erb
104
+ - app/views/solid_queue_monitor/overview/index.html.erb
105
+ - app/views/solid_queue_monitor/queues/_job_row.html.erb
106
+ - app/views/solid_queue_monitor/queues/_row.html.erb
107
+ - app/views/solid_queue_monitor/queues/index.html.erb
108
+ - app/views/solid_queue_monitor/queues/show.html.erb
109
+ - app/views/solid_queue_monitor/ready_jobs/_row.html.erb
110
+ - app/views/solid_queue_monitor/ready_jobs/index.html.erb
111
+ - app/views/solid_queue_monitor/recurring_jobs/_row.html.erb
112
+ - app/views/solid_queue_monitor/recurring_jobs/index.html.erb
113
+ - app/views/solid_queue_monitor/scheduled_jobs/_row.html.erb
114
+ - app/views/solid_queue_monitor/scheduled_jobs/index.html.erb
115
+ - app/views/solid_queue_monitor/search/_completed_row.html.erb
116
+ - app/views/solid_queue_monitor/search/_failed_row.html.erb
117
+ - app/views/solid_queue_monitor/search/_job_row.html.erb
118
+ - app/views/solid_queue_monitor/search/_recurring_row.html.erb
119
+ - app/views/solid_queue_monitor/search/_section.html.erb
120
+ - app/views/solid_queue_monitor/search/index.html.erb
121
+ - app/views/solid_queue_monitor/shared/_filters.html.erb
122
+ - app/views/solid_queue_monitor/shared/_flash.html.erb
123
+ - app/views/solid_queue_monitor/shared/_footer.html.erb
124
+ - app/views/solid_queue_monitor/shared/_header.html.erb
125
+ - app/views/solid_queue_monitor/shared/_jobs_table.html.erb
126
+ - app/views/solid_queue_monitor/shared/_pagination.html.erb
127
+ - app/views/solid_queue_monitor/workers/_row.html.erb
128
+ - app/views/solid_queue_monitor/workers/index.html.erb
89
129
  - config/database.yml
90
130
  - config/initializers/solid_queue_monitor.rb
91
131
  - config/routes.rb
@@ -1,211 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SolidQueueMonitor
4
- class BasePresenter
5
- include ActionView::Helpers::DateHelper
6
- include ActionView::Helpers::TextHelper
7
- include Rails.application.routes.url_helpers
8
- include SolidQueueMonitor::Engine.routes.url_helpers
9
-
10
- def default_url_options
11
- { only_path: true }
12
- end
13
-
14
- def section_wrapper(_title, content)
15
- <<-HTML
16
- <div class="section-wrapper">
17
- <div class="section">
18
- #{content}
19
- </div>
20
- </div>
21
- HTML
22
- end
23
-
24
- def generate_pagination(current_page, total_pages)
25
- return '' if total_pages <= 1
26
-
27
- html = '<div class="pagination">'
28
-
29
- # Previous page link
30
- html += if current_page > 1
31
- "<a href=\"?page=#{current_page - 1}#{query_params}\" class=\"pagination-link pagination-nav\">Previous</a>"
32
- else
33
- '<span class="pagination-link pagination-nav disabled">Previous</span>'
34
- end
35
-
36
- # Page links
37
- visible_pages = calculate_visible_pages(current_page, total_pages)
38
-
39
- visible_pages.each do |page|
40
- html += if page == :gap
41
- '<span class="pagination-gap">...</span>'
42
- elsif page == current_page
43
- "<span class=\"pagination-current\">#{page}</span>"
44
- else
45
- "<a href=\"?page=#{page}#{query_params}\" class=\"pagination-link\">#{page}</a>"
46
- end
47
- end
48
-
49
- # Next page link
50
- html += if current_page < total_pages
51
- "<a href=\"?page=#{current_page + 1}#{query_params}\" class=\"pagination-link pagination-nav\">Next</a>"
52
- else
53
- '<span class="pagination-link pagination-nav disabled">Next</span>'
54
- end
55
-
56
- html += '</div>'
57
- html
58
- end
59
-
60
- def calculate_visible_pages(current_page, total_pages)
61
- if total_pages <= 7
62
- (1..total_pages).to_a
63
- else
64
- case current_page
65
- when 1..3
66
- [1, 2, 3, 4, :gap, total_pages]
67
- when (total_pages - 2)..total_pages
68
- [1, :gap, total_pages - 3, total_pages - 2, total_pages - 1, total_pages]
69
- else
70
- [1, :gap, current_page - 1, current_page, current_page + 1, :gap, total_pages]
71
- end
72
- end
73
- end
74
-
75
- def format_datetime(datetime)
76
- return '-' unless datetime
77
-
78
- datetime.strftime('%Y-%m-%d %H:%M:%S')
79
- end
80
-
81
- def format_arguments(arguments)
82
- return '-' if arguments.blank?
83
-
84
- # Extract and format the arguments more cleanly
85
- formatted_args = if arguments.is_a?(Hash) && arguments['arguments'].present?
86
- format_job_arguments(arguments)
87
- elsif arguments.is_a?(Array) && arguments.length == 1 && arguments[0].is_a?(Hash) && arguments[0]['arguments'].present?
88
- format_job_arguments(arguments[0])
89
- else
90
- arguments.inspect
91
- end
92
-
93
- if formatted_args.length <= 50
94
- "<code class='args-single-line'>#{formatted_args}</code>"
95
- else
96
- <<-HTML
97
- <div class="args-container">
98
- <code class="args-content">#{formatted_args}</code>
99
- </div>
100
- HTML
101
- end
102
- end
103
-
104
- def format_hash(hash)
105
- return '-' if hash.blank?
106
-
107
- formatted = hash.map do |key, value|
108
- "<strong>#{key}:</strong> #{value.to_s.truncate(50)}"
109
- end.join(', ')
110
-
111
- "<code>#{formatted}</code>"
112
- end
113
-
114
- def queue_link(queue_name, css_class: nil)
115
- return '-' if queue_name.blank?
116
-
117
- classes = ['queue-link', css_class].compact.join(' ')
118
- "<a href=\"#{queue_details_path(queue_name: queue_name)}\" class=\"#{classes}\">#{queue_name}</a>"
119
- end
120
-
121
- def sortable_header(column, label)
122
- return "<th>#{label}</th>" unless @sort
123
-
124
- column_str = column.to_s
125
- is_active = @sort[:sort_by] == column_str
126
- next_direction = is_active && @sort[:sort_direction] == 'asc' ? 'desc' : 'asc'
127
- arrow = sort_arrow(is_active)
128
- css_class = is_active ? 'sortable-header active' : 'sortable-header'
129
-
130
- "<th><a href=\"?sort_by=#{column}&sort_direction=#{next_direction}#{filter_query_string}\" class=\"#{css_class}\">#{label}#{arrow}</a></th>"
131
- end
132
-
133
- def sort_arrow(is_active)
134
- return ' &udarr;' unless is_active
135
-
136
- @sort[:sort_direction] == 'asc' ? ' &uarr;' : ' &darr;'
137
- end
138
-
139
- def filter_query_string
140
- params = []
141
- params << "class_name=#{@filters[:class_name]}" if @filters && @filters[:class_name].present?
142
- params << "queue_name=#{@filters[:queue_name]}" if @filters && @filters[:queue_name].present?
143
- params << "arguments=#{@filters[:arguments]}" if @filters && @filters[:arguments].present?
144
- params << "status=#{@filters[:status]}" if @filters && @filters[:status].present?
145
-
146
- params.empty? ? '' : "&#{params.join('&')}"
147
- end
148
-
149
- def request_path
150
- if defined?(controller) && controller.respond_to?(:request)
151
- controller.request.path
152
- else
153
- '/solid_queue'
154
- end
155
- end
156
-
157
- def engine_mount_point
158
- path_parts = request_path.split('/')
159
- if path_parts.length >= 3
160
- "/#{path_parts[1]}/#{path_parts[2]}"
161
- else
162
- '/solid_queue'
163
- end
164
- end
165
-
166
- private
167
-
168
- def query_params
169
- params = build_filter_params + build_sort_params
170
- params.empty? ? '' : "&#{params.join('&')}"
171
- end
172
-
173
- def build_filter_params
174
- return [] unless @filters
175
-
176
- filter_keys = %i[class_name queue_name status]
177
- filter_keys.filter_map do |key|
178
- "#{key}=#{@filters[key]}" if @filters[key].present?
179
- end
180
- end
181
-
182
- def build_sort_params
183
- return [] unless @sort
184
-
185
- sort_keys = %i[sort_by sort_direction]
186
- sort_keys.filter_map do |key|
187
- "#{key}=#{@sort[key]}" if @sort[key].present?
188
- end
189
- end
190
-
191
- def full_path(route_name, *args)
192
- SolidQueueMonitor::Engine.routes.url_helpers.send(route_name, *args)
193
- rescue NoMethodError
194
- Rails.application.routes.url_helpers.send("solid_queue_#{route_name}", *args)
195
- end
196
-
197
- def format_job_arguments(job_data)
198
- args = if job_data['arguments'].is_a?(Array)
199
- if job_data['arguments'].first.is_a?(Hash) && job_data['arguments'].first['_aj_ruby2_keywords'].present?
200
- job_data['arguments'].first.except('_aj_ruby2_keywords')
201
- else
202
- job_data['arguments']
203
- end
204
- else
205
- job_data['arguments']
206
- end
207
-
208
- args.inspect
209
- end
210
- end
211
- end