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
@@ -1,144 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SolidQueueMonitor
4
- class JobsPresenter < BasePresenter
5
- include Rails.application.routes.url_helpers
6
- include SolidQueueMonitor::Engine.routes.url_helpers
7
-
8
- def initialize(jobs, current_page: 1, total_pages: 1, filters: {}, sort: {})
9
- @jobs = jobs
10
- @current_page = current_page
11
- @total_pages = total_pages
12
- @filters = filters
13
- @sort = sort
14
- end
15
-
16
- def render
17
- <<-HTML
18
- <div class="section-wrapper">
19
- <div class="section">
20
- <h3>Recent Jobs</h3>
21
- #{generate_filter_form}
22
- #{generate_table}
23
- #{generate_pagination(@current_page, @total_pages)}
24
- </div>
25
- </div>
26
- HTML
27
- end
28
-
29
- private
30
-
31
- def generate_filter_form
32
- <<-HTML
33
- <div class="filter-form-container">
34
- <form method="get" action="" class="filter-form">
35
- <div class="filter-group">
36
- <label for="class_name">Job Class:</label>
37
- <input type="text" name="class_name" id="class_name" value="#{@filters[:class_name]}" placeholder="Filter by class name">
38
- </div>
39
-
40
- <div class="filter-group">
41
- <label for="queue_name">Queue:</label>
42
- <input type="text" name="queue_name" id="queue_name" value="#{@filters[:queue_name]}" placeholder="Filter by queue">
43
- </div>
44
-
45
- <div class="filter-group">
46
- <label for="arguments">Arguments:</label>
47
- <input type="text" name="arguments" id="arguments" value="#{@filters[:arguments]}" placeholder="Filter by arguments">
48
- </div>
49
-
50
- <div class="filter-group">
51
- <label for="status">Status:</label>
52
- <select name="status" id="status">
53
- <option value="">All Statuses</option>
54
- <option value="completed" #{@filters[:status] == 'completed' ? 'selected' : ''}>Completed</option>
55
- <option value="failed" #{@filters[:status] == 'failed' ? 'selected' : ''}>Failed</option>
56
- <option value="scheduled" #{@filters[:status] == 'scheduled' ? 'selected' : ''}>Scheduled</option>
57
- <option value="pending" #{@filters[:status] == 'pending' ? 'selected' : ''}>Pending</option>
58
- </select>
59
- </div>
60
-
61
- <div class="filter-actions">
62
- <button type="submit" class="filter-button">Apply Filters</button>
63
- <a href="#{root_path}" class="reset-button">Reset</a>
64
- </div>
65
- </form>
66
- </div>
67
- HTML
68
- end
69
-
70
- def generate_table
71
- <<-HTML
72
- <div class="table-container">
73
- <table>
74
- <thead>
75
- <tr>
76
- <th>ID</th>
77
- #{sortable_header('class_name', 'Job')}
78
- #{sortable_header('queue_name', 'Queue')}
79
- <th>Arguments</th>
80
- <th>Status</th>
81
- #{sortable_header('created_at', 'Created At')}
82
- <th>Actions</th>
83
- </tr>
84
- </thead>
85
- <tbody>
86
- #{@jobs.map { |job| generate_row(job) }.join}
87
- </tbody>
88
- </table>
89
- </div>
90
- HTML
91
- end
92
-
93
- def generate_row(job)
94
- status = job_status(job)
95
-
96
- # Build the row HTML
97
- row_html = <<-HTML
98
- <tr>
99
- <td><a href="#{job_path(job)}" class="job-class-link">#{job.id}</a></td>
100
- <td><a href="#{job_path(job)}" class="job-class-link">#{job.class_name}</a></td>
101
- <td>#{queue_link(job.queue_name)}</td>
102
- <td>#{format_arguments(job.arguments)}</td>
103
- <td><span class='status-badge status-#{status}'>#{status}</span></td>
104
- <td>#{format_datetime(job.created_at)}</td>
105
- HTML
106
-
107
- # Add actions column only for failed jobs
108
- if status == 'failed'
109
- # Find the failed execution record for this job
110
- failed_execution = SolidQueue::FailedExecution.find_by(job_id: job.id)
111
-
112
- row_html += if failed_execution
113
- <<-HTML
114
- <td class="actions-cell">
115
- <div class="job-actions">
116
- <form method="post" action="#{retry_failed_job_path(id: failed_execution.id)}" class="inline-form">
117
- <input type="hidden" name="redirect_to" value="#{root_path}">
118
- <button type="submit" class="action-button retry-button">Retry</button>
119
- </form>
120
-
121
- <form method="post" action="#{discard_failed_job_path(id: failed_execution.id)}" class="inline-form"
122
- onsubmit="return confirm('Are you sure you want to discard this job?');">
123
- <input type="hidden" name="redirect_to" value="#{root_path}">
124
- <button type="submit" class="action-button discard-button">Discard</button>
125
- </form>
126
- </div>
127
- </td>
128
- HTML
129
- else
130
- '<td></td>'
131
- end
132
- else
133
- row_html += '<td></td>'
134
- end
135
-
136
- row_html += '</tr>'
137
- row_html
138
- end
139
-
140
- def job_status(job)
141
- SolidQueueMonitor::StatusCalculator.new(job).calculate
142
- end
143
- end
144
- end
@@ -1,195 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SolidQueueMonitor
4
- class QueueDetailsPresenter < BasePresenter
5
- def initialize(queue_name:, paused:, jobs:, counts:, current_page: 1, total_pages: 1, filters: {}, sort: {})
6
- @queue_name = queue_name
7
- @paused = paused
8
- @jobs = jobs
9
- @counts = counts
10
- @current_page = current_page
11
- @total_pages = total_pages
12
- @filters = filters
13
- @sort = sort
14
- end
15
-
16
- def render
17
- section_wrapper("Queue: #{@queue_name}",
18
- render_header + render_stats_cards + generate_filter_form + generate_table + generate_pagination(@current_page, @total_pages))
19
- end
20
-
21
- private
22
-
23
- def render_header
24
- <<-HTML
25
- <div class="section-header-row">
26
- <div class="section-header-left">
27
- #{status_badge}
28
- </div>
29
- <div class="section-header-right">
30
- #{action_button}
31
- </div>
32
- </div>
33
- HTML
34
- end
35
-
36
- def status_badge
37
- if @paused
38
- '<span class="status-badge status-paused">Paused</span>'
39
- else
40
- '<span class="status-badge status-active">Active</span>'
41
- end
42
- end
43
-
44
- def action_button
45
- if @paused
46
- <<-HTML
47
- <form action="#{resume_queue_path}" method="post" class="inline-form">
48
- <input type="hidden" name="queue_name" value="#{@queue_name}">
49
- <input type="hidden" name="redirect_to" value="#{queue_details_path(queue_name: @queue_name)}">
50
- <button type="submit" class="action-button resume-button">Resume Queue</button>
51
- </form>
52
- HTML
53
- else
54
- <<-HTML
55
- <form action="#{pause_queue_path}" method="post" class="inline-form"
56
- onsubmit="return confirm('Are you sure you want to pause this queue?');">
57
- <input type="hidden" name="queue_name" value="#{@queue_name}">
58
- <input type="hidden" name="redirect_to" value="#{queue_details_path(queue_name: @queue_name)}">
59
- <button type="submit" class="action-button pause-button">Pause Queue</button>
60
- </form>
61
- HTML
62
- end
63
- end
64
-
65
- def render_stats_cards
66
- <<-HTML
67
- <div class="stats-container">
68
- <div class="stats">
69
- #{generate_stat_card('Total Jobs', @counts[:total])}
70
- #{generate_stat_card('Ready', @counts[:ready])}
71
- #{generate_stat_card('Scheduled', @counts[:scheduled])}
72
- #{generate_stat_card('In Progress', @counts[:in_progress])}
73
- #{generate_stat_card('Completed', @counts[:completed])}
74
- #{generate_stat_card('Failed', @counts[:failed])}
75
- </div>
76
- </div>
77
- HTML
78
- end
79
-
80
- def generate_stat_card(title, value)
81
- <<-HTML
82
- <div class="stat-card">
83
- <h3>#{title}</h3>
84
- <p>#{value}</p>
85
- </div>
86
- HTML
87
- end
88
-
89
- def generate_filter_form
90
- <<-HTML
91
- <div class="filter-form-container">
92
- <form method="get" action="#{queue_details_path(queue_name: @queue_name)}" class="filter-form">
93
- <div class="filter-group">
94
- <label for="class_name">Job Class:</label>
95
- <input type="text" name="class_name" id="class_name" value="#{@filters[:class_name]}" placeholder="Filter by class name">
96
- </div>
97
-
98
- <div class="filter-group">
99
- <label for="arguments">Arguments:</label>
100
- <input type="text" name="arguments" id="arguments" value="#{@filters[:arguments]}" placeholder="Filter by arguments">
101
- </div>
102
-
103
- <div class="filter-group">
104
- <label for="status">Status:</label>
105
- <select name="status" id="status">
106
- <option value="">All Statuses</option>
107
- <option value="completed" #{@filters[:status] == 'completed' ? 'selected' : ''}>Completed</option>
108
- <option value="failed" #{@filters[:status] == 'failed' ? 'selected' : ''}>Failed</option>
109
- <option value="scheduled" #{@filters[:status] == 'scheduled' ? 'selected' : ''}>Scheduled</option>
110
- <option value="pending" #{@filters[:status] == 'pending' ? 'selected' : ''}>Pending</option>
111
- <option value="in_progress" #{@filters[:status] == 'in_progress' ? 'selected' : ''}>In Progress</option>
112
- </select>
113
- </div>
114
-
115
- <div class="filter-actions">
116
- <button type="submit" class="filter-button">Apply Filters</button>
117
- <a href="#{queue_details_path(queue_name: @queue_name)}" class="reset-button">Reset</a>
118
- </div>
119
- </form>
120
- </div>
121
- HTML
122
- end
123
-
124
- def generate_table
125
- return '<p class="empty-message">No jobs in this queue</p>' if @jobs.empty?
126
-
127
- <<-HTML
128
- <div class="table-container">
129
- <table>
130
- <thead>
131
- <tr>
132
- <th>ID</th>
133
- #{sortable_header('class_name', 'Job')}
134
- <th>Arguments</th>
135
- <th>Status</th>
136
- #{sortable_header('created_at', 'Created At')}
137
- <th>Actions</th>
138
- </tr>
139
- </thead>
140
- <tbody>
141
- #{@jobs.map { |job| generate_row(job) }.join}
142
- </tbody>
143
- </table>
144
- </div>
145
- HTML
146
- end
147
-
148
- def generate_row(job)
149
- status = job_status(job)
150
-
151
- row_html = <<-HTML
152
- <tr>
153
- <td><a href="#{job_path(job)}" class="job-class-link">#{job.id}</a></td>
154
- <td><a href="#{job_path(job)}" class="job-class-link">#{job.class_name}</a></td>
155
- <td>#{format_arguments(job.arguments)}</td>
156
- <td><span class="status-badge status-#{status}">#{status}</span></td>
157
- <td>#{format_datetime(job.created_at)}</td>
158
- HTML
159
-
160
- # Add actions column for failed jobs
161
- if status == 'failed'
162
- failed_execution = SolidQueue::FailedExecution.find_by(job_id: job.id)
163
-
164
- row_html += if failed_execution
165
- <<-HTML
166
- <td class="actions-cell">
167
- <div class="job-actions">
168
- <form method="post" action="#{retry_failed_job_path(id: failed_execution.id)}" class="inline-form">
169
- <input type="hidden" name="redirect_to" value="#{queue_details_path(queue_name: @queue_name)}">
170
- <button type="submit" class="action-button retry-button">Retry</button>
171
- </form>
172
- <form method="post" action="#{discard_failed_job_path(id: failed_execution.id)}" class="inline-form"
173
- onsubmit="return confirm('Are you sure you want to discard this job?');">
174
- <input type="hidden" name="redirect_to" value="#{queue_details_path(queue_name: @queue_name)}">
175
- <button type="submit" class="action-button discard-button">Discard</button>
176
- </form>
177
- </div>
178
- </td>
179
- HTML
180
- else
181
- '<td></td>'
182
- end
183
- else
184
- row_html += '<td></td>'
185
- end
186
-
187
- row_html += '</tr>'
188
- row_html
189
- end
190
-
191
- def job_status(job)
192
- SolidQueueMonitor::StatusCalculator.new(job).calculate
193
- end
194
- end
195
- end
@@ -1,89 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SolidQueueMonitor
4
- class QueuesPresenter < BasePresenter
5
- def initialize(records, paused_queues = [], sort: {}, queue_stats: {})
6
- @records = records
7
- @paused_queues = paused_queues
8
- @sort = sort
9
- @queue_stats = queue_stats
10
- end
11
-
12
- def render
13
- section_wrapper('Queues', generate_table)
14
- end
15
-
16
- private
17
-
18
- def generate_table
19
- <<-HTML
20
- <div class="table-container">
21
- <table>
22
- <thead>
23
- <tr>
24
- #{sortable_header('queue_name', 'Queue Name')}
25
- <th>Status</th>
26
- #{sortable_header('job_count', 'Total Jobs')}
27
- <th>Ready Jobs</th>
28
- <th>Scheduled Jobs</th>
29
- <th>Failed Jobs</th>
30
- <th>Actions</th>
31
- </tr>
32
- </thead>
33
- <tbody>
34
- #{@records.map { |queue| generate_row(queue) }.join}
35
- </tbody>
36
- </table>
37
- </div>
38
- HTML
39
- end
40
-
41
- def generate_row(queue)
42
- queue_name = queue.queue_name || 'default'
43
- paused = @paused_queues.include?(queue_name)
44
-
45
- <<-HTML
46
- <tr class="#{paused ? 'queue-paused' : ''}">
47
- <td>#{queue_link(queue_name)}</td>
48
- <td>#{status_badge(paused)}</td>
49
- <td>#{queue.job_count}</td>
50
- <td>#{@queue_stats.dig(:ready, queue_name) || 0}</td>
51
- <td>#{@queue_stats.dig(:scheduled, queue_name) || 0}</td>
52
- <td>#{@queue_stats.dig(:failed, queue_name) || 0}</td>
53
- <td class="actions-cell">#{action_button(queue_name, paused)}</td>
54
- </tr>
55
- HTML
56
- end
57
-
58
- def status_badge(paused)
59
- if paused
60
- '<span class="status-badge status-paused">Paused</span>'
61
- else
62
- '<span class="status-badge status-active">Active</span>'
63
- end
64
- end
65
-
66
- def action_button(queue_name, paused)
67
- if paused
68
- <<-HTML
69
- <form action="#{resume_queue_path}" method="post" class="inline-form">
70
- <input type="hidden" name="queue_name" value="#{queue_name}">
71
- <button type="submit" class="action-button resume-button" title="Resume queue processing">
72
- Resume
73
- </button>
74
- </form>
75
- HTML
76
- else
77
- <<-HTML
78
- <form action="#{pause_queue_path}" method="post" class="inline-form"
79
- onsubmit="return confirm('Are you sure you want to pause the #{queue_name} queue? Workers will stop processing jobs from this queue.');">
80
- <input type="hidden" name="queue_name" value="#{queue_name}">
81
- <button type="submit" class="action-button pause-button" title="Pause queue processing">
82
- Pause
83
- </button>
84
- </form>
85
- HTML
86
- end
87
- end
88
- end
89
- end
@@ -1,81 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SolidQueueMonitor
4
- class ReadyJobsPresenter < BasePresenter
5
- def initialize(jobs, current_page: 1, total_pages: 1, filters: {}, sort: {})
6
- @jobs = jobs
7
- @current_page = current_page
8
- @total_pages = total_pages
9
- @filters = filters
10
- @sort = sort
11
- end
12
-
13
- def render
14
- section_wrapper('Ready Jobs',
15
- generate_filter_form + generate_table + generate_pagination(@current_page, @total_pages))
16
- end
17
-
18
- private
19
-
20
- def generate_filter_form
21
- <<-HTML
22
- <div class="filter-form-container">
23
- <form method="get" action="" class="filter-form">
24
- <div class="filter-group">
25
- <label for="class_name">Job Class:</label>
26
- <input type="text" name="class_name" id="class_name" value="#{@filters[:class_name]}" placeholder="Filter by class name">
27
- </div>
28
-
29
- <div class="filter-group">
30
- <label for="queue_name">Queue:</label>
31
- <input type="text" name="queue_name" id="queue_name" value="#{@filters[:queue_name]}" placeholder="Filter by queue">
32
- </div>
33
-
34
- <div class="filter-group">
35
- <label for="arguments">Arguments:</label>
36
- <input type="text" name="arguments" id="arguments" value="#{@filters[:arguments]}" placeholder="Filter by arguments">
37
- </div>
38
-
39
- <div class="filter-actions">
40
- <button type="submit" class="filter-button">Apply Filters</button>
41
- <a href="#{ready_jobs_path}" class="reset-button">Reset</a>
42
- </div>
43
- </form>
44
- </div>
45
- HTML
46
- end
47
-
48
- def generate_table
49
- <<-HTML
50
- <div class="table-container">
51
- <table>
52
- <thead>
53
- <tr>
54
- #{sortable_header('class_name', 'Job')}
55
- #{sortable_header('queue_name', 'Queue')}
56
- #{sortable_header('priority', 'Priority')}
57
- <th>Arguments</th>
58
- #{sortable_header('created_at', 'Created At')}
59
- </tr>
60
- </thead>
61
- <tbody>
62
- #{@jobs.map { |execution| generate_row(execution) }.join}
63
- </tbody>
64
- </table>
65
- </div>
66
- HTML
67
- end
68
-
69
- def generate_row(execution)
70
- <<-HTML
71
- <tr>
72
- <td><a href="#{job_path(execution.job)}" class="job-class-link">#{execution.job.class_name}</a></td>
73
- <td>#{queue_link(execution.queue_name)}</td>
74
- <td>#{execution.priority}</td>
75
- <td>#{format_arguments(execution.job.arguments)}</td>
76
- <td>#{format_datetime(execution.created_at)}</td>
77
- </tr>
78
- HTML
79
- end
80
- end
81
- end
@@ -1,81 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SolidQueueMonitor
4
- class RecurringJobsPresenter < BasePresenter
5
- include Rails.application.routes.url_helpers
6
- include SolidQueueMonitor::Engine.routes.url_helpers
7
-
8
- def initialize(jobs, current_page: 1, total_pages: 1, filters: {}, sort: {})
9
- @jobs = jobs
10
- @current_page = current_page
11
- @total_pages = total_pages
12
- @filters = filters
13
- @sort = sort
14
- end
15
-
16
- def render
17
- section_wrapper('Recurring Jobs',
18
- generate_filter_form + generate_table + generate_pagination(@current_page, @total_pages))
19
- end
20
-
21
- private
22
-
23
- def generate_filter_form
24
- <<-HTML
25
- <div class="filter-form-container">
26
- <form method="get" action="" class="filter-form">
27
- <div class="filter-group">
28
- <label for="class_name">Job Class:</label>
29
- <input type="text" name="class_name" id="class_name" value="#{@filters[:class_name]}" placeholder="Filter by class name">
30
- </div>
31
-
32
- <div class="filter-group">
33
- <label for="queue_name">Queue:</label>
34
- <input type="text" name="queue_name" id="queue_name" value="#{@filters[:queue_name]}" placeholder="Filter by queue">
35
- </div>
36
-
37
- <div class="filter-actions">
38
- <button type="submit" class="filter-button">Apply Filters</button>
39
- <a href="#{recurring_jobs_path}" class="reset-button">Reset</a>
40
- </div>
41
- </form>
42
- </div>
43
- HTML
44
- end
45
-
46
- def generate_table
47
- <<-HTML
48
- <div class="table-container">
49
- <table>
50
- <thead>
51
- <tr>
52
- #{sortable_header('key', 'Key')}
53
- #{sortable_header('class_name', 'Job')}
54
- <th>Schedule</th>
55
- #{sortable_header('queue_name', 'Queue')}
56
- #{sortable_header('priority', 'Priority')}
57
- <th>Last Updated</th>
58
- </tr>
59
- </thead>
60
- <tbody>
61
- #{@jobs.map { |task| generate_row(task) }.join}
62
- </tbody>
63
- </table>
64
- </div>
65
- HTML
66
- end
67
-
68
- def generate_row(task)
69
- <<-HTML
70
- <tr>
71
- <td>#{task.key}</td>
72
- <td>#{task.class_name}</td>
73
- <td>#{task.schedule}</td>
74
- <td>#{queue_link(task.queue_name)}</td>
75
- <td>#{task.priority || 'Default'}</td>
76
- <td>#{format_datetime(task.updated_at)}</td>
77
- </tr>
78
- HTML
79
- end
80
- end
81
- end