good_job 3.1.0 → 3.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +62 -7
  3. data/README.md +35 -31
  4. data/app/assets/good_job/modules/application.js +2 -0
  5. data/app/assets/good_job/modules/popovers.js +7 -0
  6. data/app/assets/good_job/style.css +4 -0
  7. data/app/helpers/good_job/application_helper.rb +2 -2
  8. data/{lib → app/models/concerns}/good_job/filterable.rb +0 -0
  9. data/app/models/concerns/good_job/reportable.rb +43 -0
  10. data/{lib → app}/models/good_job/active_job_job.rb +0 -0
  11. data/{lib → app}/models/good_job/base_record.rb +0 -0
  12. data/{lib → app}/models/good_job/cron_entry.rb +1 -1
  13. data/{lib → app}/models/good_job/execution.rb +42 -45
  14. data/{lib → app/models}/good_job/execution_result.rb +0 -0
  15. data/{lib → app}/models/good_job/job.rb +2 -27
  16. data/{lib → app}/models/good_job/lockable.rb +0 -0
  17. data/{lib → app}/models/good_job/process.rb +4 -0
  18. data/app/views/good_job/cron_entries/index.html.erb +60 -57
  19. data/app/views/good_job/jobs/_executions.erb +31 -28
  20. data/app/views/good_job/jobs/_table.erb +138 -109
  21. data/app/views/good_job/jobs/show.html.erb +40 -31
  22. data/app/views/good_job/processes/index.html.erb +60 -32
  23. data/app/views/good_job/shared/_navbar.erb +1 -1
  24. data/app/views/good_job/shared/icons/_dots.html.erb +3 -0
  25. data/app/views/good_job/shared/icons/_info.html.erb +4 -0
  26. data/lib/generators/good_job/templates/install/migrations/create_good_jobs.rb.erb +4 -4
  27. data/lib/generators/good_job/templates/update/migrations/01_create_good_jobs.rb.erb +4 -4
  28. data/lib/good_job/adapter.rb +13 -11
  29. data/lib/good_job/cli.rb +4 -4
  30. data/lib/good_job/configuration.rb +6 -1
  31. data/lib/good_job/daemon.rb +2 -2
  32. data/lib/good_job/job_performer.rb +1 -1
  33. data/lib/good_job/notifier.rb +1 -0
  34. data/lib/good_job/version.rb +1 -1
  35. data/lib/good_job.rb +29 -14
  36. metadata +19 -30
  37. data/lib/good_job/active_job_extensions.rb +0 -5
@@ -1,61 +1,64 @@
1
- <div class="my-3 flex">
2
- <h2>Cron Schedules</h2>
1
+ <div class="bg-light break-out border-bottom">
2
+ <h2 class="container-fluid pt-3 pb-2">Cron Schedules</h2>
3
3
  </div>
4
4
 
5
- <% if @cron_entries.present? %>
6
- <div class="card my-3">
7
- <div class="table-responsive">
8
- <table class="table card-table table-bordered table-hover table-sm mb-0">
9
- <thead>
10
- <th>Key</th>
11
- <th>Schedule</th>
12
- <th>
13
- Properties
14
- <%= tag.button "Toggle", type: "button", class: "btn btn-sm btn-outline-primary", role: "button",
15
- data: { bs_toggle: "collapse", bs_target: ".cron-entry-properties" },
16
- aria: { expanded: false, controls: @cron_entries.map { |cron_entry| "##{dom_id(cron_entry, 'properties')}" }.join(" ") }
17
- %>
18
- </th>
19
- <th>Description</th>
20
- <th>Next scheduled</th>
21
- <th>Last run</th>
22
- <th>Actions</th>
23
- </thead>
24
- <tbody>
25
- <% @cron_entries.each do |cron_entry| %>
26
- <tr id="<%= dom_id(cron_entry) %>">
27
- <td class="font-monospace"><%= cron_entry.key %></td>
28
- <td class="font-monospace"><%= cron_entry.schedule %></td>
29
- <td>
30
- <%= tag.button("Inspect", type: "button", class: "btn btn-sm btn-outline-primary", role: "button",
31
- data: { bs_toggle: "collapse", bs_target: "##{dom_id(cron_entry, 'properties')}" },
32
- aria: { expanded: false, controls: dom_id(cron_entry, 'properties') }) %>
33
- <%= tag.pre(JSON.pretty_generate(cron_entry.display_properties), id: dom_id(cron_entry, 'properties'), class: "collapse cron-entry-properties") %>
34
- </td>
35
- <td><%= cron_entry.description %></td>
36
- <td><%= cron_entry.next_at %></td>
37
- <td>
38
- <% if cron_entry.last_job.present? %>
39
- <%= link_to cron_entry.last_at, cron_entry_path(cron_entry), title: "Job #{cron_entry.last_job.id}" %>
40
- <% end %>
41
- </td>
42
- <td>
43
- <%= button_to enqueue_cron_entry_path(cron_entry.id), method: :post, class: "btn btn-sm btn-outline-primary", form_class: "d-inline-block", aria: { label: "Run cron entry now" }, title: "Run cron entry now", data: { confirm: "Confirm run cron entry now" } do %>
44
- <%= render "good_job/shared/icons/play" %>
45
- <% end %>
46
- </td>
47
- </tr>
5
+ <div class="card my-3">
6
+ <div class="list-group list-group-flush text-nowrap" role="table">
7
+ <header class="list-group-item bg-light">
8
+ <div class="row small text-muted text-uppercase align-items-center">
9
+ <div class="col"></div>
10
+ <div class="col">Class</div>
11
+ <div class="col">Schedule</div>
12
+ <div class="col">Next scheduled</div>
13
+ <div class="col">Last run</div>
14
+ <div class="col text-end">
15
+ <%= tag.button type: "button", class: "btn btn-sm text-muted", role: "button",
16
+ data: { bs_toggle: "collapse", bs_target: ".cron-entry-properties" },
17
+ aria: { expanded: false, controls: @cron_entries.map { |cron_entry| "##{dom_id(cron_entry, 'properties')}" }.join(" ") } do %>
18
+ <%= render_icon "info" %>
19
+ <span class="visually-hidden">Inspect</span>
48
20
  <% end %>
49
- </tbody>
50
- </table>
51
- </div>
21
+ </div>
22
+ </div>
23
+ </header>
24
+ <% @cron_entries.each do |cron_entry| %>
25
+ <div id="<%= dom_id(cron_entry) %>" class="list-group-item py-3" role="row">
26
+ <div class="row align-items-center">
27
+ <div class="col">
28
+ <div class="small font-monospace"><%= cron_entry.key %></div>
29
+ <div class="small text-muted"><%= cron_entry.description %></div>
30
+ </div>
31
+ <div class="col"><%= tag.span tag.code(cron_entry.job_class), class: "fs-5 mb-0" %></div>
32
+ <div class="col font-monospace fw-bold"><%= cron_entry.schedule %></div>
33
+ <div class="col small"><%= relative_time cron_entry.next_at %></div>
34
+ <div class="col small">
35
+ <% if cron_entry.last_job.present? %>
36
+ <%= link_to relative_time(cron_entry.last_at), cron_entry_path(cron_entry), title: "Job #{cron_entry.last_job.id}" %>
37
+ <% end %>
38
+ </div>
39
+ <div class="col d-flex gap-3 justify-content-end">
40
+ <%= button_to enqueue_cron_entry_path(cron_entry.id), method: :post, class: "btn btn-sm btn-outline-primary", form_class: "d-inline-block", aria: { label: "Run cron entry now" }, title: "Run cron entry now", data: { confirm: "Confirm run cron entry now" } do %>
41
+ <%= render "good_job/shared/icons/play" %>
42
+ Run Now
43
+ <% end %>
44
+ <%= tag.button type: "button", class: "btn btn-sm text-muted", role: "button",
45
+ title: "Inspect",
46
+ data: { bs_toggle: "collapse", bs_target: "##{dom_id(cron_entry, 'properties')}" },
47
+ aria: { expanded: false, controls: dom_id(cron_entry, "properties") } do %>
48
+ <%= render_icon "info" %>
49
+ <span class="visually-hidden">Inspect</span>
50
+ <% end %>
51
+ </div>
52
+ </div>
53
+ </div>
54
+ <%= tag.div id: dom_id(cron_entry, 'properties'), class: "collapse cron-entry-properties list-group-item collapse small bg-dark text-light" do %>
55
+ <%= tag.pre JSON.pretty_generate(cron_entry.display_properties) %>
56
+ <% end %>
57
+ <% end %>
58
+ <% if @cron_entries.empty? %>
59
+ <div class="list-group-item py-4 text-center text-muted">
60
+ No cron schedules found.
61
+ </div>
62
+ <% end %>
52
63
  </div>
53
- <% else %>
54
- <div class="card my-3">
55
- <div class="card-body">
56
- <p class="card-text">
57
- <em>No cron schedules found.</em>
58
- </p>
59
- </div>
60
- </div>
61
- <% end %>
64
+ </div>
@@ -3,43 +3,46 @@
3
3
  <div class="list-group list-group-flush">
4
4
  <% executions.each do |execution| %>
5
5
  <%= tag.div id: dom_id(execution), class: "list-group-item py-3" do %>
6
- <div class="d-md-flex">
7
- <div class="flex-fill">
8
- <div class="small text-muted">
9
- #<%= execution.number %>:
10
- <%= tag.code link_to(execution.id, "##{dom_id(execution)}", class: "text-muted text-decoration-none") %>
11
- </div>
12
- <div class="d-flex gap-2 align-items-center text-muted mt-1">
6
+ <div class="row align-items-center text-nowrap">
7
+ <div class="col-5 d-flex gap-2">
8
+ <%= tag.span execution.number, class: "badge bg-secondary bg-opacity-50 rounded-pill" %>
9
+ <%= tag.code link_to(execution.id, "##{dom_id(execution)}", class: "text-muted text-decoration-none small") %>
10
+ </div>
11
+ <div class="col-2 small">
12
+ <% if execution.queue_latency %>
13
+ <%= format_duration execution.queue_latency %> <span class="text-muted">in queue</span>
14
+ <% end %>
15
+ </div>
16
+ <div class="col-2 small">
17
+ <% if execution.runtime_latency %>
18
+ <%= format_duration execution.runtime_latency %> <span class="text-muted">runtime</span>
19
+ <% end %>
20
+ </div>
21
+ <div class="col">
22
+ <div class="d-flex gap-3 align-items-center justify-content-end">
23
+ <%= tag.span relative_time(execution.last_status_at, include_seconds: true), class: "small" %>
13
24
  <%= status_badge execution.status %>
14
- <%= relative_time execution.last_status_at, include_seconds: true %>
15
-
16
- <% if execution.runtime_latency %>
17
- • <div><%= format_duration execution.runtime_latency %> runtime</div>
18
- <% end %>
19
- <% if execution.queue_latency %>
20
- • <div><%= format_duration execution.queue_latency %> in queue</div>
21
- <% end %>
22
25
  </div>
23
26
  </div>
24
- <div>
25
- <div class="mt-4 d-flex gap-2">
26
- <%= tag.button type: "button", class: "btn btn-sm btn-outline-primary", role: "button",
27
- data: { bs_toggle: "collapse", bs_target: "##{dom_id(execution, 'params')}" },
28
- aria: { expanded: false, controls: dom_id(execution, "params") } do %>
29
- Inspect
30
- <% end %>
31
- </div>
27
+ <div class="col-auto">
28
+ <%= tag.button type: "button", class: "btn btn-sm text-muted ms-auto", role: "button",
29
+ title: "Inspect",
30
+ data: { bs_toggle: "collapse", bs_target: "##{dom_id(execution, 'params')}" },
31
+ aria: { expanded: false, controls: dom_id(execution, "params") } do %>
32
+ <%= render_icon "info" %>
33
+ <span class="visually-hidden">Inspect</span>
34
+ <% end %>
32
35
  </div>
33
36
  </div>
34
37
  <% if execution.error %>
35
- <div class="mt-3">
38
+ <div class="mt-3 small">
36
39
  <strong class="small">Error:</strong>
37
- <pre class="text-wrap text-break m-0"><%= execution.error %></pre>
40
+ <code class="text-wrap text-break m-0 text-black"><%= execution.error %></code>
38
41
  </div>
39
42
  <% end %>
40
- <div>
41
- <%= tag.pre JSON.pretty_generate(execution.display_serialized_params), id: dom_id(execution, "params"), class: "collapse bg-light card card-body p-3 my-3" %>
42
- </div>
43
+ <% end %>
44
+ <%= tag.div id: dom_id(execution, "params"), class: "list-group-item collapse small bg-dark text-light" do %>
45
+ <%= tag.pre JSON.pretty_generate(execution.display_serialized_params) %>
43
46
  <% end %>
44
47
  <% end %>
45
48
  </div>
@@ -1,122 +1,151 @@
1
- <div class="my-3">
2
- <div class="table-responsive">
3
- <%= form_with(url: mass_update_jobs_path(filter.to_params), method: :put, local: true, data: { "checkbox-toggle": "job_ids" }) do |form| %>
4
- <table class="table table-hover table-sm mb-0 table-jobs">
5
- <thead>
6
- <tr>
7
- <th><%= check_box_tag('toggle_job_ids', "1", false, data: { "checkbox-toggle-all": "job_ids" }) %></th>
8
- <th>ActiveJob ID</th>
9
- <th>State</th>
10
- <th>Job Class</th>
11
- <th>Queue</th>
12
- <th>Scheduled At</th>
13
- <th>Executions</th>
14
- <th>Error</th>
15
- <th>
16
- Parameters&nbsp;
17
- <%= tag.button "Toggle", type: "button", class: "btn btn-sm btn-outline-primary", role: "button",
18
- data: { bs_toggle: "collapse", bs_target: ".job-params" },
19
- aria: { expanded: false, controls: jobs.map { |job| "##{dom_id(job, "params")}" }.join(" ") }
20
- %>
21
- </th>
22
- <th>
23
- Actions<br>
1
+ <%= form_with(url: mass_update_jobs_path(filter.to_params), method: :put, local: true, data: { "checkbox-toggle": "job_ids" }) do |form| %>
2
+ <div class="my-3 card" data-gj-poll-replace id="jobs-table">
3
+ <div class="list-group list-group-flush text-nowrap table-jobs" role="table">
4
+ <header class="list-group-item bg-light">
5
+ <div class="row small text-muted text-uppercase align-items-center">
6
+ <div class="col-auto">
7
+ <%= check_box_tag('toggle_job_ids', "1", false, data: { "checkbox-toggle-all": "job_ids" }) %>
8
+ </div>
9
+ <div class="col-4">
10
+ <%= form.button type: 'submit', name: 'mass_action', value: 'reschedule', class: 'btn btn-sm btn-outline-secondary', title: "Reschedule all", data: { confirm: "Are you sure you want to reschedule the selected jobs?", disable: true } do %>
11
+ <span class="me-1"><%= render_icon "skip_forward" %></span> Reschedule
12
+ <% end %>
24
13
 
25
- <div class="d-inline text-nowrap">
26
- <%= form.button type: 'submit', name: 'mass_action', value: 'reschedule', class: 'btn btn-sm btn-outline-primary', title: "Reschedule all", data: { confirm: "Confirm reschedule all", disable: true } do %>
27
- <%= render_icon "skip_forward" %> All
28
- <% end %>
14
+ <%= form.button type: 'submit', name: 'mass_action', value: 'retry', class: 'btn btn-sm btn-outline-secondary', title: "Retry all", data: { confirm: "Are you sure you want to retry the selected jobs?", disable: true } do %>
15
+ <span class="me-1"><%= render_icon "arrow_clockwise" %></span> Retry
16
+ <% end %>
29
17
 
30
- <%= form.button type: 'submit', name: 'mass_action', value: 'discard', class: 'btn btn-sm btn-outline-primary', title: "Discard all", data: { confirm: "Confirm discard all", disable: true } do %>
31
- <%= render_icon "stop" %> All
18
+ <div class="btn-group" role="group">
19
+ <%= form.button type: 'submit', name: 'mass_action', value: 'discard', class: 'btn btn-sm btn-outline-secondary', title: "Discard all", data: { confirm: "Are you usure you want to discard the selected jobs?", disable: true } do %>
20
+ <span class="me-1"><%= render_icon "stop" %></span> Discard
21
+ <% end %>
22
+ <button id="destroy-dropdown-toggle" type="button" class="btn btn-sm btn-outline-secondary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
23
+ <span class="visually-hidden">Toggle Actions</span>
24
+ </button>
25
+ <div class="dropdown-menu" aria-labelledby="destroy-dropdown-toggle">
26
+ <li>
27
+ <%= form.button type: 'submit', name: 'mass_action', value: 'destroy', class: 'btn', title: "Destroy all", data: { confirm: "Are you sure you want to destroy the selected jobs?", disable: true } do %>
28
+ <span class="me-1"><%= render_icon "trash" %></span> Destroy
32
29
  <% end %>
30
+ </li>
31
+ </div>
32
+ </div>
33
33
 
34
- <%= form.button type: 'submit', name: 'mass_action', value: 'retry', class: 'btn btn-sm btn-outline-primary', title: "Retry all", data: { confirm: "Confirm retry all", disable: true } do %>
35
- <%= render_icon "arrow_clockwise" %> All
36
- <% end %>
34
+ </div>
35
+ <div class="col-1">Queue</div>
36
+ <div class="col-1">Priority</div>
37
+ <div class="col-1 text-end">Attempts</div>
38
+ <div class="col text-end">
39
+ <%= tag.button type: "button", class: "btn btn-sm text-muted", role: "button",
40
+ data: { bs_toggle: "collapse", bs_target: ".job-params" },
41
+ aria: { expanded: false, controls: jobs.map { |job| "##{dom_id(job, "params")}" }.join(" ") } do %>
42
+ <%= render_icon "info" %>
43
+ <span class="visually-hidden">Inspect</span>
44
+ <% end %>
45
+ </div>
46
+ </div>
47
+ </header>
48
+ <label role="row" class="list-group-item list-group-item-warning list-group-item-action py-3 d-none" data-checkbox-toggle-show="job_ids">
49
+ <div class="row">
50
+ <div class="col-auto">
51
+ <%= check_box_tag "all_job_ids", 1, false, disabled: true, data: { "checkbox-toggle-show": "job_ids"} %>
52
+ </div>
53
+ <div class="col-auto">
54
+ Apply to all <%= filter.filtered_count %> <%= "job".pluralize(filter.filtered_count) %>.
55
+ </div>
56
+ </div>
57
+ </label>
37
58
 
38
- <%= form.button type: 'submit', name: 'mass_action', value: 'destroy', class: 'btn btn-sm btn-outline-primary', title: "Destroy all", data: { confirm: "Confirm destroy all", disable: true } do %>
39
- <%= render_icon "trash" %> All
40
- <% end %>
41
- </div>
42
- </th>
43
- </tr>
44
- <tr class="d-none" data-checkbox-toggle-show="job_ids">
45
- <td class="text-center table-warning" colspan="10">
46
- <label>
47
- <%= check_box_tag "all_job_ids", 1, false, disabled: true, data: { "checkbox-toggle-show": "job_ids"} %>
48
- Apply to all <%= filter.filtered_count %> <%= "job".pluralize(filter.filtered_count) %>.
49
- </label>
50
- </td>
51
- </tr>
52
- </thead>
53
- <tbody>
54
- <% if jobs.present? %>
55
- <% jobs.each do |job| %>
56
- <tr class="<%= dom_class(job) %>" id="<%= dom_id(job) %>">
57
- <td><%= check_box_tag 'job_ids[]', job.id, false, data: { "checkbox-toggle-each": "job_ids" } %></td>
58
- <td>
59
- <%= link_to job_path(job.id) do %>
60
- <code><%= job.id %></code>
61
- <% end %>
62
- </td>
63
- <td><%= status_badge(job.status) %></td>
64
- <td><%= job.job_class %></td>
65
- <td><%= job.queue_name %></td>
66
- <td><%= relative_time(job.scheduled_at || job.created_at) %></td>
67
- <td><%= job.executions_count %></td>
68
- <td class="text-break"><%= truncate(job.recent_error, length: 1_000) %></td>
69
- <td>
70
- <%= tag.button "Inspect", type: "button", class: "btn btn-sm btn-outline-primary", role: "button",
71
- data: { bs_toggle: "collapse", bs_target: "##{dom_id(job, 'params')}" },
72
- aria: { expanded: false, controls: dom_id(job, "params") }
73
- %>
74
- <%= tag.pre JSON.pretty_generate(job.display_serialized_params), id: dom_id(job, "params"), class: "collapse job-params" %>
75
- </td>
76
- <td>
77
- <div class="text-nowrap">
78
- <% if job.status.in? [:scheduled, :retried, :queued] %>
79
- <%= link_to reschedule_job_path(job.id), method: :put, class: "btn btn-sm btn-outline-primary", title: "Reschedule job", data: { confirm: "Confirm reschedule", disable: true } do %>
80
- <%= render_icon "skip_forward" %>
81
- <% end %>
82
- <% else %>
83
- <button class="btn btn-sm btn-outline-secondary" disabled><%= render_icon "skip_forward" %></button>
59
+ <% if jobs.present? %>
60
+ <% jobs.each do |job| %>
61
+ <%= label_tag dom_id(job, :checkbox), id: dom_id(job), role: "row", class: "list-group-item list-group-item-action py-3" do %>
62
+ <div class="row align-items-center">
63
+ <div class="col-auto">
64
+ <%= check_box_tag 'job_ids[]', job.id, false, id: dom_id(job, :checkbox), data: { "checkbox-toggle-each": "job_ids" } %>
65
+ </div>
66
+ <div class="col-4">
67
+ <%= tag.code link_to(job.id, job_path(job), class: "small text-muted text-decoration-none") %>
68
+ <%= tag.h5 tag.code(link_to(job.job_class, job_path(job), class: "text-reset text-decoration-none")), class: "text-reset mb-0" %>
69
+ </div>
70
+ <div class="col-1">
71
+ <span class="badge bg-primary bg-opacity-25 text-dark font-monospace"><%= job.queue_name %></span>
72
+ </div>
73
+ <div class="col-1 small text-center">
74
+ <span class="font-monospace fw-bold"><%= job.priority %></span>
75
+ </div>
76
+ <div class="col-1 text-center">
77
+ <% if job.executions_count > 0 && job.status != :finished %>
78
+ <%= tag.span job.executions_count, class: "badge rounded-pill bg-danger", data: {
79
+ bs_toggle: "popover",
80
+ bs_trigger: "hover focus click",
81
+ bs_placement: "bottom",
82
+ bs_content: job.recent_error
83
+ } %>
84
+ <% else %>
85
+ <span class="badge bg-secondary bg-opacity-50 rounded-pill"><%= job.executions_count %></span>
86
+ <% end %>
87
+ </div>
88
+ <div class="col d-flex gap-3 align-items-center justify-content-end">
89
+ <%= tag.span relative_time(job.last_status_at), class: "small" %>
90
+ <%= status_badge job.status %>
91
+ </div>
92
+ <div class="col-auto">
93
+ <div class="dropdown float-end">
94
+ <button class="d-flex align-items-center btn btn-sm" type="button" id="<%= dom_id(job, :actions) %>" data-bs-toggle="dropdown" aria-expanded="false">
95
+ <%= render "good_job/shared/icons/dots" %>
96
+ <span class="visually-hidden">Actions</span>
97
+ </button>
98
+ <ul class="dropdown-menu shadow" aria-labelledby="<%= dom_id(job, :actions) %>">
99
+ <li>
100
+ <% job_reschedulable = job.status.in? [:scheduled, :retried, :queued] %>
101
+ <%= link_to reschedule_job_path(job.id), method: :put, class: "dropdown-item #{'disabled' unless job_reschedulable}", title: "Reschedule job", data: { confirm: "Confirm reschedule", disable: true } do %>
102
+ <%= render "good_job/shared/icons/skip_forward" %>
103
+ Reschedule
84
104
  <% end %>
85
-
86
- <% if job.status.in? [:scheduled, :retried, :queued] %>
87
- <%= link_to discard_job_path(job.id), method: :put, class: "btn btn-sm btn-outline-primary", title: "Discard job", data: { confirm: "Confirm discard", disable: true } do %>
88
- <%= render_icon "stop" %>
89
- <% end %>
90
- <% else %>
91
- <button class="btn btn-sm btn-outline-secondary" disabled><%= render_icon "stop" %></button>
105
+ </li>
106
+ <li>
107
+ <% job_discardable = job.status.in? [:scheduled, :retried, :queued] %>
108
+ <%= link_to discard_job_path(job.id), method: :put, class: "dropdown-item #{'disabled' unless job_discardable}", title: "Discard job", data: { confirm: "Confirm discard", disable: true } do %>
109
+ <%= render "good_job/shared/icons/stop" %>
110
+ Discard
92
111
  <% end %>
93
-
94
- <% if job.status == :discarded %>
95
- <%= link_to retry_job_path(job.id), method: :put, class: "btn btn-sm btn-outline-primary", title: "Retry job", data: { confirm: "Confirm retry", disable: true } do %>
96
- <%= render_icon "arrow_clockwise" %>
97
- <% end %>
98
- <% else %>
99
- <button class="btn btn-sm btn-outline-secondary" disabled><%= render_icon "arrow_clockwise" %></button>
112
+ </li>
113
+ <li>
114
+ <%= link_to retry_job_path(job.id), method: :put, class: "dropdown-item #{'disabled' unless job.status == :discarded}", title: "Retry job", data: { confirm: "Confirm retry", disable: true } do %>
115
+ <%= render "good_job/shared/icons/arrow_clockwise" %>
116
+ Retry
117
+ <% end %>
118
+ </li>
119
+ <li>
120
+ <%= link_to job_path(job.id), method: :delete, class: "dropdown-item #{'disabled' unless job.status.in? [:discarded, :finished]}", title: "Destroy job", data: { confirm: "Confirm destroy", disable: true } do %>
121
+ <%= render_icon "trash" %>
122
+ Destroy
100
123
  <% end %>
124
+ </li>
101
125
 
102
- <% if job.status.in? [:discarded, :finished] %>
103
- <%= link_to job_path(job.id), method: :delete, class: "btn btn-sm btn-outline-primary", title: "Destroy job", data: { confirm: "Confirm destroy", disable: true } do %>
104
- <%= render_icon "trash" %>
105
- <% end %>
106
- <% else %>
107
- <button class="btn btn-sm btn-outline-secondary" disabled><%= render_icon "trash" %></button>
126
+ <li>
127
+ <%= link_to "##{dom_id(job, 'params')}",
128
+ class: "dropdown-item",
129
+ data: { bs_toggle: "collapse" },
130
+ aria: { expanded: false, controls: dom_id(job, "params") } do %>
131
+ <%= render_icon "info" %>
132
+ Inspect
108
133
  <% end %>
109
- </div>
110
- </td>
111
- </tr>
112
- <% end %>
113
- <% else %>
114
- <tr>
115
- <td colspan="10" class="py-2 text-center text-muted">No jobs found.</td>
116
- </tr>
117
- <% end %>
118
- </tbody>
119
- </table>
134
+ </li>
135
+ </ul>
136
+ </div>
137
+ </div>
138
+ </div>
139
+ <% end %>
140
+ <%= tag.div id: dom_id(job, "params"), class: "job-params list-group-item collapse small bg-dark text-light" do %>
141
+ <%= tag.pre JSON.pretty_generate(job.display_serialized_params) %>
142
+ <% end %>
143
+ <% end %>
144
+ <% else %>
145
+ <div class="list-group-item py-4 text-center text-muted">
146
+ No jobs found.
147
+ </div>
120
148
  <% end %>
121
149
  </div>
122
150
  </div>
151
+ <% end %>
@@ -1,48 +1,57 @@
1
1
  <div class="break-out bg-light border-bottom py-2 mb-3">
2
2
  <div class="container-fluid pt-2">
3
- <div class="d-flex align-items-center">
4
- <div class="flex-fill">
3
+ <div class="row align-items-center">
4
+ <div class="col-5">
5
5
  <nav aria-label="breadcrumb">
6
6
  <ol class="breadcrumb small mb-0">
7
7
  <li class="breadcrumb-item"><%= link_to "Jobs", jobs_path %></li>
8
- <li class="breadcrumb-item active" aria-current="page">ActiveJob ID: <%= tag.code @job.id %></li>
8
+ <li class="breadcrumb-item active" aria-current="page"><%= tag.code @job.id, class: "text-muted" %></li>
9
9
  </ol>
10
10
  </nav>
11
- <h2 class="mb-1"><%= tag.code @job.job_class %></h2>
12
- <div class="text-muted small d-flex gap-2">
13
- <div>Queue: <%= tag.strong @job.queue_name %></div>
14
-
15
- <div>Priority: <%= tag.strong @job.priority %></div>
11
+ <h2 class="mb-0"><%= tag.code @job.job_class %></h2>
12
+ </div>
13
+ <div class="col-2">
14
+ <div class="small text-muted text-uppercase">Queue</div>
15
+ <div class="badge bg-primary bg-opacity-25 text-dark font-monospace my-2">
16
+ <%= tag.strong @job.queue_name %>
16
17
  </div>
17
18
  </div>
18
- <div>
19
- <% job_reschedulable = @job.status.in? [:scheduled, :retried, :queued] %>
20
- <%= button_to reschedule_job_path(@job.id), method: :put,
21
- class: "btn btn-sm #{job_reschedulable ? 'btn-outline-primary' : 'btn-outline-secondary'}",
22
- form_class: "d-inline-block",
23
- disabled: !job_reschedulable,
24
- aria: { label: "Reschedule job" },
25
- title: "Reschedule job",
26
- data: { confirm: "Confirm reschedule" } do %>
27
- <%= render_icon "skip_forward" %>
28
- Reschedule
19
+ <div class="col-2">
20
+ <div class="small text-muted text-uppercase">Priority</div>
21
+ <div class="font-monospace fw-bold small my-2"><%= tag.strong @job.priority %></div>
22
+ </div>
23
+ <div class="col text-end">
24
+ <% if @job.status.in? [:scheduled, :retried, :queued] %>
25
+ <%= button_to reschedule_job_path(@job.id), method: :put,
26
+ class: "btn btn-sm btn-outline-primary",
27
+ form_class: "d-inline-block",
28
+ aria: { label: "Reschedule job" },
29
+ title: "Reschedule job",
30
+ data: { confirm: "Confirm reschedule" } do %>
31
+ <%= render_icon "skip_forward" %>
32
+ Reschedule
33
+ <% end %>
29
34
  <% end %>
30
35
 
31
- <% job_discardable = @job.status.in? [:scheduled, :retried, :queued] %>
32
- <%= button_to discard_job_path(@job.id), method: :put, class: "btn btn-sm #{job_discardable ? 'btn-outline-primary' : 'btn-outline-secondary'}", form_class: "d-inline-block", disabled: !job_discardable, aria: { label: "Discard job" }, title: "Discard job", data: { confirm: "Confirm discard" } do %>
33
- <%= render_icon "stop" %>
34
- Discard
36
+ <% if @job.status.in? [:scheduled, :retried, :queued] %>
37
+ <%= button_to discard_job_path(@job.id), method: :put, class: "btn btn-sm btn-outline-primary", form_class: "d-inline-block", aria: { label: "Discard job" }, title: "Discard job", data: { confirm: "Confirm discard" } do %>
38
+ <%= render_icon "stop" %>
39
+ Discard
40
+ <% end %>
35
41
  <% end %>
36
42
 
37
- <%= button_to retry_job_path(@job.id), method: :put, class: "btn btn-sm #{@job.status == :discarded ? 'btn-outline-primary' : 'btn-outline-secondary'}", form_class: "d-inline-block", disabled: @job.status != :discarded, aria: { label: "Retry job" }, title: "Retry job", data: { confirm: "Confirm retry" } do %>
38
- <%= render_icon "arrow_clockwise" %>
39
- Retry
43
+ <% if @job.status == :discarded %>
44
+ <%= button_to retry_job_path(@job.id), method: :put, class: "btn btn-sm btn-outline-primary", form_class: "d-inline-block", aria: { label: "Retry job" }, title: "Retry job", data: { confirm: "Confirm retry" } do %>
45
+ <%= render_icon "arrow_clockwise" %>
46
+ Retry
47
+ <% end %>
40
48
  <% end %>
41
49
 
42
- <% job_destroyable = @job.status.in? [:discarded, :finished] %>
43
- <%= button_to job_path(@job.id), method: :delete, class: "btn btn-sm #{job_destroyable ? 'btn-outline-primary' : 'btn-outline-secondary'}", form_class: "d-inline-block", disabled: !job_destroyable, aria: { label: "Destroy job" }, title: "Destroy job", data: { confirm: "Confirm destroy" } do %>
44
- <%= render_icon "trash" %>
45
- Destroy
50
+ <% if @job.status.in? [:discarded, :finished] %>
51
+ <%= button_to job_path(@job.id), method: :delete, class: "btn btn-sm btn-outline-primary", form_class: "d-inline-block", aria: { label: "Destroy job" }, title: "Destroy job", data: { confirm: "Confirm destroy" } do %>
52
+ <%= render_icon "trash" %>
53
+ Destroy
54
+ <% end %>
46
55
  <% end %>
47
56
  </div>
48
57
  </div>
@@ -51,7 +60,7 @@
51
60
 
52
61
  <div class="my-4">
53
62
  <h5>Arguments</h5>
54
- <%= tag.pre @job.serialized_params["arguments"].map(&:inspect).join(', ') %>
63
+ <%= tag.pre @job.serialized_params["arguments"].map(&:inspect).join(', '), class: 'text-wrap text-break' %>
55
64
  </div>
56
65
 
57
66
  <%= render 'executions', executions: @job.executions.includes_advisory_locks.reverse %>