solid_queue_web 0.5.0 → 0.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 313999fb3e2f6307b5f5e65bd8be047935bc04894d4bc1cfb94f2b87e945f9ac
4
- data.tar.gz: d107e00893b0f8cd655d096c59778bb171b1c10c4f6716d4ec88182957ba0dfc
3
+ metadata.gz: 4fede0428a11872f9c6d461a4c58b658c59a5dbb4549de6fe0f13d1f1bafd6ae
4
+ data.tar.gz: 3fd89b402ec8034cdcec4e80c260e83dedcf4c96519375283fe0678696548d88
5
5
  SHA512:
6
- metadata.gz: 796462cdaf3a6c1e40daf0fe0f9d398f9ae3e095ff22dadd2938330d2159f6cce468770582a114a52bf68de9f5eb05ce2336c8ca333bd5e3e8effa62d3f58707
7
- data.tar.gz: 6b30554ce77a3daf354848bd7c80eea666fe8dda09b487ec246ccd7ab47adbc9ae42e43f6c82c6a73da3918a9d93f3ee48a5bcc5c126aac49079d1b5de24e47b
6
+ metadata.gz: 361aaa9eca22abd7a34f574c434f04700dcce5d0c0f29dd0bf4f929bccedf08b32db280db3c8e29c6b237325b791dc09528cced2f9d8419b8f02879e32a0aa36
7
+ data.tar.gz: f2d79da345dec941c24a468729f78de4b210d9f4d8bb6b8203c6e60da16577e7b5538c2b66b8a544524bbc32cf7289fc26a77e3529e27444f825b889afde582a
@@ -1,9 +1,9 @@
1
1
  module SolidQueueWeb
2
2
  class FailedJobsController < ApplicationController
3
+ before_action :set_filter_params, only: [ :index, :retry_all, :discard_all ]
4
+
3
5
  def index
4
- @pagy, @failed_jobs = pagy(
5
- SolidQueue::FailedExecution.includes(:job).order(created_at: :desc)
6
- )
6
+ @pagy, @failed_jobs = pagy(filtered_scope.order(created_at: :desc))
7
7
  end
8
8
 
9
9
  def retry
@@ -23,16 +23,31 @@ module SolidQueueWeb
23
23
  end
24
24
 
25
25
  def retry_all
26
- executions = SolidQueue::FailedExecution.includes(:job).to_a
27
- jobs = executions.map(&:job)
26
+ jobs = filtered_scope.map(&:job)
28
27
  SolidQueue::FailedExecution.retry_all(jobs)
29
- redirect_to failed_jobs_path, notice: "#{jobs.size} #{"job".pluralize(jobs.size)} queued for retry."
28
+ redirect_to failed_jobs_path(queue: @queue, q: @search),
29
+ notice: "#{jobs.size} #{"job".pluralize(jobs.size)} queued for retry."
30
30
  end
31
31
 
32
32
  def discard_all
33
- count = SolidQueue::FailedExecution.count
34
- SolidQueue::FailedExecution.discard_all_in_batches
35
- redirect_to failed_jobs_path, notice: "#{count} #{"job".pluralize(count)} discarded."
33
+ jobs = filtered_scope.map(&:job)
34
+ SolidQueue::FailedExecution.discard_all_from_jobs(jobs)
35
+ redirect_to failed_jobs_path(queue: @queue, q: @search),
36
+ notice: "#{jobs.size} #{"job".pluralize(jobs.size)} discarded."
37
+ end
38
+
39
+ private
40
+
41
+ def set_filter_params
42
+ @queue = params[:queue].presence
43
+ @search = params[:q].presence
44
+ end
45
+
46
+ def filtered_scope
47
+ scope = SolidQueue::FailedExecution.includes(:job)
48
+ scope = scope.references(:job).where(solid_queue_jobs: { queue_name: @queue }) if @queue.present?
49
+ scope = scope.references(:job).where("solid_queue_jobs.class_name LIKE ?", "%#{@search}%") if @search.present?
50
+ scope
36
51
  end
37
52
  end
38
53
  end
@@ -1,23 +1,11 @@
1
1
  module SolidQueueWeb
2
2
  class JobsController < ApplicationController
3
- before_action :set_status_and_queue, only: [ :destroy, :discard_all ]
4
-
5
- STATUSES = %w[ready scheduled claimed blocked failed].freeze
6
- DISCARDABLE = %w[ready scheduled blocked].freeze
7
- EXECUTION_MODELS = {
8
- "ready" => SolidQueue::ReadyExecution,
9
- "scheduled" => SolidQueue::ScheduledExecution,
10
- "claimed" => SolidQueue::ClaimedExecution,
11
- "blocked" => SolidQueue::BlockedExecution,
12
- "failed" => SolidQueue::FailedExecution
13
- }.freeze
3
+ before_action :set_status, only: [ :destroy, :discard_all ]
14
4
 
15
5
  def index
16
- @status = params[:status].presence_in(STATUSES) || "ready"
17
- @queue = params[:queue].presence
6
+ @status = params[:status].presence_in(Job::STATUSES) || "ready"
18
7
  @search = params[:q].presence
19
- @jobs = EXECUTION_MODELS[@status].includes(:job)
20
- @jobs = @jobs.where(jobs: { queue_name: @queue }) if @queue.present?
8
+ @jobs = Job::EXECUTION_MODELS[@status].includes(:job)
21
9
  @jobs = @jobs.references(:job).where("solid_queue_jobs.class_name LIKE ?", "%#{@search}%") if @search.present?
22
10
  @pagy, @jobs = pagy(@jobs.order(created_at: :desc))
23
11
  end
@@ -26,7 +14,6 @@ module SolidQueueWeb
26
14
  @job = SolidQueue::Job
27
15
  .includes(:ready_execution, :scheduled_execution, :claimed_execution, :blocked_execution, :failed_execution)
28
16
  .find(params[:id])
29
- @failed_execution = @job.failed_execution
30
17
  @execution_status = derive_status(@job)
31
18
  end
32
19
 
@@ -37,24 +24,24 @@ module SolidQueueWeb
37
24
  @remaining_count = filtered_scope(model).count
38
25
  respond_to do |format|
39
26
  format.turbo_stream
40
- format.html { redirect_to jobs_path(status: @status, queue: @queue), notice: "Job discarded." }
27
+ format.html { redirect_to jobs_path(status: @status), notice: "Job discarded." }
41
28
  end
42
29
  rescue ArgumentError => e
43
- redirect_to jobs_path(status: @status, queue: @queue), alert: e.message
30
+ redirect_to jobs_path(status: @status), alert: e.message
44
31
  rescue => e
45
- redirect_to jobs_path(status: @status, queue: @queue), alert: "Could not discard job: #{e.message}"
32
+ redirect_to jobs_path(status: @status), alert: "Could not discard job: #{e.message}"
46
33
  end
47
34
 
48
35
  def discard_all
49
36
  model = execution_model_for!(@status)
50
37
  jobs = filtered_scope(model).map(&:job)
51
38
  model.discard_all_from_jobs(jobs)
52
- redirect_to jobs_path(status: @status, queue: @queue),
39
+ redirect_to jobs_path(status: @status),
53
40
  notice: "#{jobs.size} #{"job".pluralize(jobs.size)} discarded."
54
41
  rescue ArgumentError => e
55
- redirect_to jobs_path(status: @status, queue: @queue), alert: e.message
42
+ redirect_to jobs_path(status: @status), alert: e.message
56
43
  rescue => e
57
- redirect_to jobs_path(status: @status, queue: @queue), alert: "Could not discard jobs: #{e.message}"
44
+ redirect_to jobs_path(status: @status), alert: "Could not discard jobs: #{e.message}"
58
45
  end
59
46
 
60
47
  private
@@ -68,19 +55,17 @@ module SolidQueueWeb
68
55
  "finished"
69
56
  end
70
57
 
71
- def set_status_and_queue
58
+ def set_status
72
59
  @status = params[:status]
73
- @queue = params[:queue].presence
74
60
  end
75
61
 
76
62
  def filtered_scope(model)
77
- scope = model.includes(:job)
78
- @queue.present? ? scope.where(jobs: { queue_name: @queue }) : scope
63
+ model.includes(:job)
79
64
  end
80
65
 
81
66
  def execution_model_for!(status)
82
- raise ArgumentError, "Cannot discard #{status} jobs from this page." unless DISCARDABLE.include?(status)
83
- EXECUTION_MODELS[status]
67
+ raise ArgumentError, "Cannot discard #{status} jobs from this page." unless Job::DISCARDABLE.include?(status)
68
+ Job::EXECUTION_MODELS[status]
84
69
  end
85
70
  end
86
71
  end
@@ -0,0 +1,63 @@
1
+ module SolidQueueWeb
2
+ module Queues
3
+ class JobsController < ApplicationController
4
+ before_action :set_queue
5
+ before_action :set_status, only: [ :destroy, :discard_all ]
6
+
7
+ def index
8
+ @status = params[:status].presence_in(Job::STATUSES) || "ready"
9
+ @search = params[:q].presence
10
+ @jobs = Job::EXECUTION_MODELS[@status].includes(:job)
11
+ .where(solid_queue_jobs: { queue_name: @queue })
12
+ @jobs = @jobs.references(:job).where("solid_queue_jobs.class_name LIKE ?", "%#{@search}%") if @search.present?
13
+ @pagy, @jobs = pagy(@jobs.order(created_at: :desc))
14
+ end
15
+
16
+ def destroy
17
+ model = execution_model_for!(@status)
18
+ @execution = model.find(params[:id])
19
+ @execution.discard
20
+ @remaining_count = filtered_scope(model).count
21
+ respond_to do |format|
22
+ format.turbo_stream
23
+ format.html { redirect_to queue_jobs_path(queue_name: @queue, status: @status), notice: "Job discarded." }
24
+ end
25
+ rescue ArgumentError => e
26
+ redirect_to queue_jobs_path(queue_name: @queue, status: @status), alert: e.message
27
+ rescue => e
28
+ redirect_to queue_jobs_path(queue_name: @queue, status: @status), alert: "Could not discard job: #{e.message}"
29
+ end
30
+
31
+ def discard_all
32
+ model = execution_model_for!(@status)
33
+ jobs = filtered_scope(model).map(&:job)
34
+ model.discard_all_from_jobs(jobs)
35
+ redirect_to queue_jobs_path(queue_name: @queue, status: @status),
36
+ notice: "#{jobs.size} #{"job".pluralize(jobs.size)} discarded."
37
+ rescue ArgumentError => e
38
+ redirect_to queue_jobs_path(queue_name: @queue, status: @status), alert: e.message
39
+ rescue => e
40
+ redirect_to queue_jobs_path(queue_name: @queue, status: @status), alert: "Could not discard jobs: #{e.message}"
41
+ end
42
+
43
+ private
44
+
45
+ def set_queue
46
+ @queue = params[:queue_name]
47
+ end
48
+
49
+ def set_status
50
+ @status = params[:status]
51
+ end
52
+
53
+ def filtered_scope(model)
54
+ model.includes(:job).where(solid_queue_jobs: { queue_name: @queue })
55
+ end
56
+
57
+ def execution_model_for!(status)
58
+ raise ArgumentError, "Cannot discard #{status} jobs from this page." unless Job::DISCARDABLE.include?(status)
59
+ Job::EXECUTION_MODELS[status]
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,13 @@
1
+ module SolidQueueWeb
2
+ class Job
3
+ STATUSES = %w[ready scheduled claimed blocked failed].freeze
4
+ DISCARDABLE = %w[ready scheduled blocked].freeze
5
+ EXECUTION_MODELS = {
6
+ "ready" => SolidQueue::ReadyExecution,
7
+ "scheduled" => SolidQueue::ScheduledExecution,
8
+ "claimed" => SolidQueue::ClaimedExecution,
9
+ "blocked" => SolidQueue::BlockedExecution,
10
+ "failed" => SolidQueue::FailedExecution
11
+ }.freeze
12
+ end
13
+ end
@@ -2,14 +2,33 @@
2
2
  <h1 class="sqd-page-title">Failed Jobs</h1>
3
3
  <% if @failed_jobs.any? %>
4
4
  <div class="sqd-actions">
5
- <%= button_to "Retry All", retry_all_failed_jobs_path, method: :post, class: "sqd-btn sqd-btn--primary",
5
+ <%= button_to "Retry All", retry_all_failed_jobs_path,
6
+ method: :post,
7
+ params: { queue: @queue, q: @search },
8
+ class: "sqd-btn sqd-btn--primary",
6
9
  data: { confirm: "Retry all #{@failed_jobs.size} failed jobs?" } %>
7
- <%= button_to "Discard All", discard_all_failed_jobs_path, method: :post, class: "sqd-btn sqd-btn--danger",
10
+ <%= button_to "Discard All", discard_all_failed_jobs_path,
11
+ method: :post,
12
+ params: { queue: @queue, q: @search },
13
+ class: "sqd-btn sqd-btn--danger",
8
14
  data: { confirm: "Discard all #{@failed_jobs.size} failed jobs? This cannot be undone." } %>
9
15
  </div>
10
16
  <% end %>
11
17
  </div>
12
18
 
19
+ <form class="sqd-search" action="<%= failed_jobs_path %>" method="get" data-controller="search">
20
+ <% if @queue.present? %>
21
+ <input type="hidden" name="queue" value="<%= @queue %>">
22
+ <% end %>
23
+ <input class="sqd-search__input" type="search" name="q" value="<%= @search %>"
24
+ placeholder="Filter by job class…" autocomplete="off" aria-label="Filter by job class"
25
+ data-action="input->search#filter">
26
+ <button type="submit" class="sqd-btn sqd-btn--muted">Search</button>
27
+ <% if @search.present? %>
28
+ <%= link_to "Clear", failed_jobs_path(queue: @queue), class: "sqd-btn sqd-btn--muted" %>
29
+ <% end %>
30
+ </form>
31
+
13
32
  <% if @pagy.last > 1 %>
14
33
  <%= @pagy.series_nav.html_safe %>
15
34
  <% end %>
@@ -33,7 +52,10 @@
33
52
  <% job = execution.job %>
34
53
  <tr>
35
54
  <td><%= link_to job.class_name, job_path(job) %></td>
36
- <td class="sqd-mono"><%= job.queue_name %></td>
55
+ <td>
56
+ <%= link_to job.queue_name, failed_jobs_path(queue: job.queue_name, q: @search),
57
+ class: "sqd-mono", style: "color: inherit;" %>
58
+ </td>
37
59
  <td>
38
60
  <% if execution.exception_class.present? %>
39
61
  <div class="sqd-error-msg sqd-truncate" title="<%= execution.exception_class %>: <%= execution.message %>">
@@ -56,4 +78,11 @@
56
78
  </tbody>
57
79
  </table>
58
80
  <% end %>
59
- </div>
81
+ </div>
82
+
83
+ <% if @queue.present? %>
84
+ <p style="margin-top: 0.75rem; font-size: 13px; color: var(--muted);">
85
+ Filtering by queue: <strong><%= @queue %></strong> &mdash;
86
+ <%= link_to "Clear filter", failed_jobs_path(q: @search) %>
87
+ </p>
88
+ <% end %>
@@ -1,21 +1,21 @@
1
1
  <h1 class="sqd-page-title" style="margin-bottom: 1.5rem;">Jobs</h1>
2
2
 
3
3
  <%= turbo_frame_tag "jobs-table", data: { turbo_action: "advance" } do %>
4
- <% discardable = SolidQueueWeb::JobsController::DISCARDABLE.include?(@status) %>
4
+ <% discardable = SolidQueueWeb::Job::DISCARDABLE.include?(@status) %>
5
5
 
6
6
  <div class="sqd-page-header">
7
7
  <div class="sqd-filters">
8
- <%= link_to "Ready", jobs_path(status: "ready", queue: @queue, q: @search), class: @status == "ready" ? "active" : "" %>
9
- <%= link_to "Scheduled", jobs_path(status: "scheduled", queue: @queue, q: @search), class: @status == "scheduled" ? "active" : "" %>
10
- <%= link_to "Running", jobs_path(status: "claimed", queue: @queue, q: @search), class: @status == "claimed" ? "active" : "" %>
11
- <%= link_to "Blocked", jobs_path(status: "blocked", queue: @queue, q: @search), class: @status == "blocked" ? "active" : "" %>
12
- <%= link_to "Failed", jobs_path(status: "failed", queue: @queue, q: @search), class: @status == "failed" ? "active" : "" %>
8
+ <%= link_to "Ready", jobs_path(status: "ready", q: @search), class: @status == "ready" ? "active" : "" %>
9
+ <%= link_to "Scheduled", jobs_path(status: "scheduled", q: @search), class: @status == "scheduled" ? "active" : "" %>
10
+ <%= link_to "Running", jobs_path(status: "claimed", q: @search), class: @status == "claimed" ? "active" : "" %>
11
+ <%= link_to "Blocked", jobs_path(status: "blocked", q: @search), class: @status == "blocked" ? "active" : "" %>
12
+ <%= link_to "Failed", jobs_path(status: "failed", q: @search), class: @status == "failed" ? "active" : "" %>
13
13
  </div>
14
14
  <% if discardable && @jobs.any? %>
15
15
  <div class="sqd-actions">
16
16
  <%= button_to "Discard All", discard_all_jobs_path,
17
17
  method: :post,
18
- params: { status: @status, queue: @queue },
18
+ params: { status: @status },
19
19
  class: "sqd-btn sqd-btn--danger",
20
20
  data: { confirm: "Discard all #{@jobs.size} #{@status} jobs? This cannot be undone." } %>
21
21
  </div>
@@ -24,15 +24,12 @@
24
24
 
25
25
  <form class="sqd-search" action="<%= jobs_path %>" method="get" data-controller="search">
26
26
  <input type="hidden" name="status" value="<%= @status %>">
27
- <% if @queue.present? %>
28
- <input type="hidden" name="queue" value="<%= @queue %>">
29
- <% end %>
30
27
  <input class="sqd-search__input" type="search" name="q" value="<%= @search %>"
31
28
  placeholder="Filter by job class…" autocomplete="off" aria-label="Filter by job class"
32
29
  data-action="input->search#filter">
33
30
  <button type="submit" class="sqd-btn sqd-btn--muted">Search</button>
34
31
  <% if @search.present? %>
35
- <%= link_to "Clear", jobs_path(status: @status, queue: @queue), class: "sqd-btn sqd-btn--muted" %>
32
+ <%= link_to "Clear", jobs_path(status: @status), class: "sqd-btn sqd-btn--muted" %>
36
33
  <% end %>
37
34
  </form>
38
35
 
@@ -57,10 +54,10 @@
57
54
  <tr id="execution_<%= execution.id %>">
58
55
  <td>
59
56
  <span class="sqd-badge sqd-badge--<%= @status %>"><%= @status %></span>
60
- <%= link_to job.class_name, job_path(job), style: "margin-left: 0.5rem;" %>
57
+ <%= link_to job.class_name, job_path(job), style: "margin-left: 0.5rem;", data: { turbo_frame: "_top" } %>
61
58
  </td>
62
59
  <td>
63
- <%= link_to job.queue_name, jobs_path(status: @status, queue: job.queue_name),
60
+ <%= link_to job.queue_name, queue_jobs_path(queue_name: job.queue_name, status: @status),
64
61
  class: "sqd-mono", style: "color: inherit;" %>
65
62
  </td>
66
63
  <td><%= job.priority %></td>
@@ -72,7 +69,7 @@
72
69
  <td class="sqd-row-actions">
73
70
  <%= button_to "Discard", job_path(execution),
74
71
  method: :delete,
75
- params: { status: @status, queue: @queue },
72
+ params: { status: @status },
76
73
  class: "sqd-btn sqd-btn--danger sqd-btn--sm",
77
74
  data: { confirm: "Discard this job?" } %>
78
75
  </td>
@@ -87,11 +84,4 @@
87
84
  <% if @pagy.last > 1 %>
88
85
  <%= @pagy.series_nav.html_safe %>
89
86
  <% end %>
90
-
91
- <% if @queue.present? %>
92
- <p style="margin-top: 0.75rem; font-size: 13px; color: var(--muted);">
93
- Filtering by queue: <strong><%= @queue %></strong> &mdash;
94
- <%= link_to "Clear filter", jobs_path(status: @status) %>
95
- </p>
96
- <% end %>
97
87
  <% end %>
@@ -7,13 +7,13 @@
7
7
  </div>
8
8
 
9
9
  <div class="sqd-actions">
10
- <% if @execution_status == "failed" && @failed_execution %>
11
- <%= button_to "Retry", retry_failed_job_path(@failed_execution), method: :post,
10
+ <% if @execution_status == "failed" && @job.failed_execution %>
11
+ <%= button_to "Retry", retry_failed_job_path(@job.failed_execution), method: :post,
12
12
  class: "sqd-btn sqd-btn--primary" %>
13
- <%= button_to "Discard", failed_job_path(@failed_execution), method: :delete,
13
+ <%= button_to "Discard", failed_job_path(@job.failed_execution), method: :delete,
14
14
  class: "sqd-btn sqd-btn--danger",
15
15
  data: { confirm: "Discard this job?" } %>
16
- <% elsif SolidQueueWeb::JobsController::DISCARDABLE.include?(@execution_status) %>
16
+ <% elsif SolidQueueWeb::Job::DISCARDABLE.include?(@execution_status) %>
17
17
  <% execution = @job.public_send("#{@execution_status}_execution") %>
18
18
  <% if execution %>
19
19
  <%= button_to "Discard", job_path(execution),
@@ -45,6 +45,11 @@
45
45
  <dt>Concurrency Key</dt>
46
46
  <dd class="sqd-mono"><%= @job.concurrency_key.presence || "—" %></dd>
47
47
 
48
+ <% if @job.blocked_execution %>
49
+ <dt>Blocked Until</dt>
50
+ <dd class="sqd-mono"><%= @job.blocked_execution.expires_at ? @job.blocked_execution.expires_at.strftime("%Y-%m-%d %H:%M:%S %Z") : "—" %></dd>
51
+ <% end %>
52
+
48
53
  <dt>Enqueued At</dt>
49
54
  <dd class="sqd-mono"><%= @job.created_at.strftime("%Y-%m-%d %H:%M:%S %Z") %></dd>
50
55
 
@@ -62,14 +67,14 @@
62
67
  </div>
63
68
  </div>
64
69
 
65
- <% if @failed_execution %>
70
+ <% if @job.failed_execution %>
66
71
  <div class="sqd-card sqd-detail-section" style="margin-top: 1.5rem;">
67
72
  <h2 class="sqd-section-title sqd-section-title--danger">Error</h2>
68
73
  <p class="sqd-error-header">
69
- <strong><%= @failed_execution.exception_class %></strong>: <%= @failed_execution.message %>
74
+ <strong><%= @job.failed_execution.exception_class %></strong>: <%= @job.failed_execution.message %>
70
75
  </p>
71
- <% if @failed_execution.backtrace.present? %>
72
- <pre class="sqd-pre sqd-pre--muted" style="margin-top: 0.75rem;"><%= Array(@failed_execution.backtrace).join("\n") %></pre>
76
+ <% if @job.failed_execution.backtrace.present? %>
77
+ <pre class="sqd-pre sqd-pre--muted" style="margin-top: 0.75rem;"><%= Array(@job.failed_execution.backtrace).join("\n") %></pre>
73
78
  <% end %>
74
79
  </div>
75
80
  <% end %>
@@ -0,0 +1,9 @@
1
+ <% if @remaining_count == 0 %>
2
+ <%= turbo_stream.replace "jobs-list" do %>
3
+ <div class="sqd-card" id="jobs-list">
4
+ <div class="sqd-empty">No <%= @status %> jobs in <%= @queue %>.</div>
5
+ </div>
6
+ <% end %>
7
+ <% else %>
8
+ <%= turbo_stream.remove "execution_#{@execution.id}" %>
9
+ <% end %>
@@ -0,0 +1,89 @@
1
+ <div class="sqd-page-header">
2
+ <div>
3
+ <div class="sqd-breadcrumb">
4
+ <%= link_to "Queues", queues_path %> &rsaquo; <%= @queue %>
5
+ </div>
6
+ <h1 class="sqd-page-title">Jobs</h1>
7
+ </div>
8
+ </div>
9
+
10
+ <%= turbo_frame_tag "jobs-table", data: { turbo_action: "advance" } do %>
11
+ <% discardable = SolidQueueWeb::Job::DISCARDABLE.include?(@status) %>
12
+
13
+ <div class="sqd-page-header">
14
+ <div class="sqd-filters">
15
+ <%= link_to "Ready", queue_jobs_path(queue_name: @queue, status: "ready", q: @search), class: @status == "ready" ? "active" : "" %>
16
+ <%= link_to "Scheduled", queue_jobs_path(queue_name: @queue, status: "scheduled", q: @search), class: @status == "scheduled" ? "active" : "" %>
17
+ <%= link_to "Running", queue_jobs_path(queue_name: @queue, status: "claimed", q: @search), class: @status == "claimed" ? "active" : "" %>
18
+ <%= link_to "Blocked", queue_jobs_path(queue_name: @queue, status: "blocked", q: @search), class: @status == "blocked" ? "active" : "" %>
19
+ <%= link_to "Failed", queue_jobs_path(queue_name: @queue, status: "failed", q: @search), class: @status == "failed" ? "active" : "" %>
20
+ </div>
21
+ <% if discardable && @jobs.any? %>
22
+ <div class="sqd-actions">
23
+ <%= button_to "Discard All", discard_all_queue_jobs_path(queue_name: @queue),
24
+ method: :post,
25
+ params: { status: @status },
26
+ class: "sqd-btn sqd-btn--danger",
27
+ data: { confirm: "Discard all #{@jobs.size} #{@status} jobs in #{@queue}? This cannot be undone." } %>
28
+ </div>
29
+ <% end %>
30
+ </div>
31
+
32
+ <form class="sqd-search" action="<%= queue_jobs_path(queue_name: @queue) %>" method="get" data-controller="search">
33
+ <input type="hidden" name="status" value="<%= @status %>">
34
+ <input class="sqd-search__input" type="search" name="q" value="<%= @search %>"
35
+ placeholder="Filter by job class…" autocomplete="off" aria-label="Filter by job class"
36
+ data-action="input->search#filter">
37
+ <button type="submit" class="sqd-btn sqd-btn--muted">Search</button>
38
+ <% if @search.present? %>
39
+ <%= link_to "Clear", queue_jobs_path(queue_name: @queue, status: @status), class: "sqd-btn sqd-btn--muted" %>
40
+ <% end %>
41
+ </form>
42
+
43
+ <div class="sqd-card" id="jobs-list">
44
+ <% if @jobs.empty? %>
45
+ <div class="sqd-empty">No <%= @status %> jobs in <%= @queue %>.</div>
46
+ <% else %>
47
+ <table>
48
+ <thead>
49
+ <tr>
50
+ <th scope="col">Job Class</th>
51
+ <th scope="col">Priority</th>
52
+ <th scope="col">Scheduled At</th>
53
+ <th scope="col">Enqueued At</th>
54
+ <% if discardable %><th scope="col"><span class="sqd-sr-only">Actions</span></th><% end %>
55
+ </tr>
56
+ </thead>
57
+ <tbody>
58
+ <% @jobs.each do |execution| %>
59
+ <% job = execution.job %>
60
+ <tr id="execution_<%= execution.id %>">
61
+ <td>
62
+ <span class="sqd-badge sqd-badge--<%= @status %>"><%= @status %></span>
63
+ <%= link_to job.class_name, job_path(job), style: "margin-left: 0.5rem;", data: { turbo_frame: "_top" } %>
64
+ </td>
65
+ <td><%= job.priority %></td>
66
+ <td class="sqd-mono">
67
+ <%= job.scheduled_at ? job.scheduled_at.strftime("%Y-%m-%d %H:%M:%S") : "—" %>
68
+ </td>
69
+ <td class="sqd-mono"><%= job.created_at.strftime("%Y-%m-%d %H:%M:%S") %></td>
70
+ <% if discardable %>
71
+ <td class="sqd-row-actions">
72
+ <%= button_to "Discard", queue_job_path(queue_name: @queue, id: execution),
73
+ method: :delete,
74
+ params: { status: @status },
75
+ class: "sqd-btn sqd-btn--danger sqd-btn--sm",
76
+ data: { confirm: "Discard this job?" } %>
77
+ </td>
78
+ <% end %>
79
+ </tr>
80
+ <% end %>
81
+ </tbody>
82
+ </table>
83
+ <% end %>
84
+ </div>
85
+
86
+ <% if @pagy.last > 1 %>
87
+ <%= @pagy.series_nav.html_safe %>
88
+ <% end %>
89
+ <% end %>
data/config/routes.rb CHANGED
@@ -8,6 +8,11 @@ SolidQueueWeb::Engine.routes.draw do
8
8
  post :pause
9
9
  post :resume
10
10
  end
11
+ resources :jobs, path: "list", only: [ :index, :destroy ], controller: "queues/jobs" do
12
+ collection do
13
+ post :discard_all
14
+ end
15
+ end
11
16
  end
12
17
  resources :jobs, path: "list", only: [ :index, :show, :destroy ] do
13
18
  collection do
@@ -1,3 +1,3 @@
1
1
  module SolidQueueWeb
2
- VERSION = "0.5.0"
2
+ VERSION = "0.5.5"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solid_queue_web
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.5.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chuck Smith
@@ -97,6 +97,7 @@ files:
97
97
  - app/controllers/solid_queue_web/failed_jobs_controller.rb
98
98
  - app/controllers/solid_queue_web/jobs_controller.rb
99
99
  - app/controllers/solid_queue_web/processes_controller.rb
100
+ - app/controllers/solid_queue_web/queues/jobs_controller.rb
100
101
  - app/controllers/solid_queue_web/queues_controller.rb
101
102
  - app/controllers/solid_queue_web/recurring_tasks_controller.rb
102
103
  - app/helpers/solid_queue_web/application_helper.rb
@@ -104,6 +105,7 @@ files:
104
105
  - app/javascript/solid_queue_web/search_controller.js
105
106
  - app/jobs/solid_queue_web/application_job.rb
106
107
  - app/models/solid_queue_web/application_record.rb
108
+ - app/models/solid_queue_web/job.rb
107
109
  - app/views/layouts/solid_queue_web/application.html.erb
108
110
  - app/views/solid_queue_web/dashboard/index.html.erb
109
111
  - app/views/solid_queue_web/failed_jobs/index.html.erb
@@ -112,6 +114,8 @@ files:
112
114
  - app/views/solid_queue_web/jobs/show.html.erb
113
115
  - app/views/solid_queue_web/processes/index.html.erb
114
116
  - app/views/solid_queue_web/queues/index.html.erb
117
+ - app/views/solid_queue_web/queues/jobs/destroy.turbo_stream.erb
118
+ - app/views/solid_queue_web/queues/jobs/index.html.erb
115
119
  - app/views/solid_queue_web/recurring_tasks/index.html.erb
116
120
  - config/importmap.rb
117
121
  - config/routes.rb