good_job 3.12.0 → 3.12.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eeb15f65ff801785a0df591dec95a0e044a4f5d7e61e6b2483003c4b2afd8cb3
4
- data.tar.gz: e5c631765c1303d1fee0597313bbcb5d3844b85ad49600f2609c83149885872d
3
+ metadata.gz: caa82da35ecdc7a40488de110b0e52b0a0e073abd8ca6e0cf877b3df4f770fce
4
+ data.tar.gz: 2bb351a541183ccd8f5b5d58209c09813ecc09cebafd4cea1daad462a66337fc
5
5
  SHA512:
6
- metadata.gz: bf0ed8436074ca82d718b64a76b4b1ce36f4bc2a085fe765b04eb101ae7886d0823bb50faa7816fd3a0dcb56d308dc568a7cdd86373d0afcb49bc2d22c045105
7
- data.tar.gz: 8bd8e67ebfcb2b90a7e1ce6a542cf659e8d4cd37f31782a3a3cd5d26cfedba1e85e9a57835a8a9e78cd731f304b055c21802ca1c68fc2fcffb32ec9b3050ebdf
6
+ metadata.gz: a15ac0a81f3efcc6184ed7717430f67aa59d857a873ced5824922e36f499a275ec141636edbb80a3a7c587a9fdd64c3b98c3c189d09d4db45807829957259ca6
7
+ data.tar.gz: 3b948c09db4ca2a60a90770937960db944e2569b3f54fc570e597f97a3b8da3ee247b96d734e0d593be87eac9de0b3a75c20e08daa77590dd94be9d6b3219f36
data/CHANGELOG.md CHANGED
@@ -1,10 +1,45 @@
1
1
  # Changelog
2
2
 
3
+ ## [v3.12.2](https://github.com/bensheldon/good_job/tree/v3.12.2) (2023-02-16)
4
+
5
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v3.12.1...v3.12.2)
6
+
7
+ **Fixed bugs:**
8
+
9
+ - Ensure all i18n locale keys are under `good_job` namespace [\#850](https://github.com/bensheldon/good_job/pull/850) ([bensheldon](https://github.com/bensheldon))
10
+
11
+ **Closed issues:**
12
+
13
+ - Override locales and conflict with rails-i18n [\#847](https://github.com/bensheldon/good_job/issues/847)
14
+
15
+ **Merged pull requests:**
16
+
17
+ - Add rack-mini-profiler to development [\#848](https://github.com/bensheldon/good_job/pull/848) ([bensheldon](https://github.com/bensheldon))
18
+ - Fix typo of "class" in README [\#845](https://github.com/bensheldon/good_job/pull/845) ([mthadley](https://github.com/mthadley))
19
+ - spelling: add n to 'not' [\#841](https://github.com/bensheldon/good_job/pull/841) ([michaelglass](https://github.com/michaelglass))
20
+
21
+ ## [v3.12.1](https://github.com/bensheldon/good_job/tree/v3.12.1) (2023-02-09)
22
+
23
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v3.12.0...v3.12.1)
24
+
25
+ **Fixed bugs:**
26
+
27
+ - Fix "NoMethodError: private method `\_good\_job\_concurrency\_key' if key is nil" [\#836](https://github.com/bensheldon/good_job/pull/836) ([bensheldon](https://github.com/bensheldon))
28
+
29
+ **Closed issues:**
30
+
31
+ - NoMethodError: private method `\_good\_job\_concurrency\_key' if key is nil [\#835](https://github.com/bensheldon/good_job/issues/835)
32
+ - Jobs go back into the queued state when a worker is killed [\#821](https://github.com/bensheldon/good_job/issues/821)
33
+
34
+ **Merged pull requests:**
35
+
36
+ - Dashboard's tables update [\#834](https://github.com/bensheldon/good_job/pull/834) ([Ajmal](https://github.com/Ajmal))
37
+
3
38
  ## [v3.12.0](https://github.com/bensheldon/good_job/tree/v3.12.0) (2023-02-07)
4
39
 
5
40
  [Full Changelog](https://github.com/bensheldon/good_job/compare/v3.11.1...v3.12.0)
6
41
 
7
- **Merged pull requests:**
42
+ **Implemented enhancements:**
8
43
 
9
44
  - Create `InterruptErrors` extension to raise an exception when an interrupted job is retried [\#830](https://github.com/bensheldon/good_job/pull/830) ([bensheldon](https://github.com/bensheldon))
10
45
 
data/README.md CHANGED
@@ -834,7 +834,7 @@ might also be configured to use (deprecated now) `ActionMailer::DeliveryJob`.
834
834
 
835
835
  Jobs will be automatically retried if the process is interrupted while performing a job, for example as the result of a `SIGKILL` or power failure.
836
836
 
837
- If you need more control over interrupt-caused retries, include the `GoodJob::ActiveJobExtensions::InterruptErrors` extension in your job closs. When an interrupted job is retried, the extension will raise a `GoodJob::InterruptError` exception within the job, which allows you to use ActiveJob's `retry_on` and `discard_on` to control the behavior of the job.
837
+ If you need more control over interrupt-caused retries, include the `GoodJob::ActiveJobExtensions::InterruptErrors` extension in your job class. When an interrupted job is retried, the extension will raise a `GoodJob::InterruptError` exception within the job, which allows you to use ActiveJob's `retry_on` and `discard_on` to control the behavior of the job.
838
838
 
839
839
  ```ruby
840
840
  class MyJob < ApplicationJob
@@ -5,20 +5,21 @@ module GoodJob
5
5
  return unless sec
6
6
 
7
7
  if sec < 1
8
- t 'duration.milliseconds', ms: (sec * 1000).floor
8
+ t 'good_job.duration.milliseconds', ms: (sec * 1000).floor
9
9
  elsif sec < 10
10
- t 'duration.less_than_10_seconds', sec: sec.floor
10
+ t 'good_job.duration.less_than_10_seconds', sec: sec.floor
11
11
  elsif sec < 60
12
- t 'duration.seconds', sec: sec.floor
12
+ t 'good_job.duration.seconds', sec: sec.floor
13
13
  elsif sec < 3600
14
- t 'duration.minutes', min: (sec / 60).floor, sec: (sec % 60).floor
14
+ t 'good_job.duration.minutes', min: (sec / 60).floor, sec: (sec % 60).floor
15
15
  else
16
- t 'duration.hours', hour: (sec / 3600).floor, min: ((sec % 3600) / 60).floor
16
+ t 'good_job.duration.hours', hour: (sec / 3600).floor, min: ((sec % 3600) / 60).floor
17
17
  end
18
18
  end
19
19
 
20
- def relative_time(timestamp, **args)
21
- text = timestamp.future? ? "in #{time_ago_in_words(timestamp, **args)}" : "#{time_ago_in_words(timestamp, **args)} ago"
20
+ def relative_time(timestamp, **options)
21
+ options = options.reverse_merge({ scope: "good_job.datetime.distance_in_words" })
22
+ text = timestamp.future? ? "in #{time_ago_in_words(timestamp, **options)}" : "#{time_ago_in_words(timestamp, **options)} ago"
22
23
  tag.time(text, datetime: timestamp, title: timestamp)
23
24
  end
24
25
 
@@ -3,9 +3,9 @@
3
3
  <header class="list-group-item bg-light">
4
4
  <div class="row small text-muted text-uppercase align-items-center">
5
5
  <div class="col-4">Jobs</div>
6
- <div class="d-none d-md-block col-md-1">Queue</div>
7
- <div class="d-none d-md-block col-md-1">Priority</div>
8
- <div class="d-none d-md-block col-md-1 text-end">Attempts</div>
6
+ <div class="d-none d-lg-block col-lg-1 text-lg-center">Queue</div>
7
+ <div class="d-none d-lg-block col-lg-1 text-lg-end">Priority</div>
8
+ <div class="d-none d-lg-block col-lg-1 text-lg-end">Attempts</div>
9
9
  <div class="col text-end">
10
10
  <%= tag.button type: "button", class: "btn btn-sm text-muted", role: "button",
11
11
  data: { bs_toggle: "collapse", bs_target: ".job-params" },
@@ -21,18 +21,20 @@
21
21
  <% jobs.each do |job| %>
22
22
  <div role="row" class="list-group-item list-group-item-action py-3">
23
23
  <div class="row align-items-center">
24
- <div class="col-md-4">
24
+ <div class="col-lg-4">
25
25
  <%= tag.code link_to(job.id, job_path(job), class: "small text-muted text-decoration-none") %>
26
26
  <%= tag.h5 tag.code(link_to(job.job_class, job_path(job), class: "text-reset text-decoration-none")), class: "text-reset mb-0" %>
27
27
  </div>
28
- <div class="col-md-1">
28
+ <div class="col-4 col-lg-1 text-lg-center">
29
+ <div class="d-lg-none small text-muted mt-1">Queue</div>
29
30
  <span class="badge bg-primary bg-opacity-25 text-dark font-monospace"><%= job.queue_name %></span>
30
31
  </div>
31
- <div class="col-md-1 small text-md-center">
32
+ <div class="col-4 col-lg-1 text-lg-end">
33
+ <div class="d-lg-none small text-muted mt-1">Priority</div>
32
34
  <span class="font-monospace fw-bold"><%= job.priority %></span>
33
- <span class="d-md-none">Priority</span>
34
35
  </div>
35
- <div class="col-md-1 text-md-center">
36
+ <div class="col-4 col-lg-1 text-lg-end">
37
+ <div class="d-lg-none small text-muted mt-1">Attempts</div>
36
38
  <% if job.executions_count > 0 && job.status != :finished %>
37
39
  <%= tag.span job.executions_count, class: "badge rounded-pill bg-danger", data: {
38
40
  bs_toggle: "popover",
@@ -43,13 +45,11 @@
43
45
  <% else %>
44
46
  <span class="badge bg-secondary bg-opacity-50 rounded-pill"><%= job.executions_count %></span>
45
47
  <% end %>
46
- <span class="d-md-none small">Attemp</span>
47
48
  </div>
48
- <div class="col d-flex gap-3 align-items-center justify-content-end">
49
+ <div class="mt-3 mt-lg-0 col d-flex gap-3 align-items-center justify-content-end">
49
50
  <%= tag.span relative_time(job.last_status_at), class: "small" %>
50
51
  <%= status_badge job.status %>
51
- </div>
52
- <div class="col-auto">
52
+
53
53
  <div class="dropdown float-end">
54
54
  <button class="d-flex align-items-center btn btn-sm" type="button" id="<%= dom_id(job, :actions) %>" data-bs-toggle="dropdown" aria-expanded="false">
55
55
  <%= render "good_job/shared/icons/dots" %>
@@ -106,5 +106,5 @@
106
106
  No jobs found.
107
107
  </div>
108
108
  <% end %>
109
- </>
109
+ </div>
110
110
  </div>
@@ -3,11 +3,11 @@
3
3
  <header class="list-group-item bg-light">
4
4
  <div class="row small text-muted text-uppercase align-items-center">
5
5
  <div class="col-4">Name</div>
6
- <div class="col-md-1 d-none d-md-block">Created</div>
7
- <div class="col-md-1 d-none d-md-block">Enqueued</div>
8
- <div class="col-md-1 d-none d-md-block">Discarded</div>
9
- <div class="col-md-1 d-none d-md-block">Finished</div>
10
- <div class="col-md-1 d-none d-md-block">Jobs</div>
6
+ <div class="col-lg-1 d-none d-lg-block">Created</div>
7
+ <div class="col-lg-1 d-none d-lg-block">Enqueued</div>
8
+ <div class="col-lg-1 d-none d-lg-block">Discarded</div>
9
+ <div class="col-lg-1 d-none d-lg-block">Finished</div>
10
+ <div class="col-lg-1 d-none d-lg-block">Jobs</div>
11
11
  <div class="col text-end">
12
12
  <%= tag.button type: "button", class: "btn btn-sm text-muted", role: "button",
13
13
  data: { bs_toggle: "collapse", bs_target: ".batch-properties" },
@@ -23,7 +23,7 @@
23
23
  <% batches.each do |batch| %>
24
24
  <div id="<%= dom_id(batch) %>" class="list-group-item py-3" role="row">
25
25
  <div class="row align-items-center">
26
- <div class="col-4">
26
+ <div class="col-12 col-lg-4">
27
27
  <%= link_to batch_path(batch), class: "text-decoration-none" do %>
28
28
  <code class="small text-muted">
29
29
  <%= batch.id %>
@@ -32,34 +32,34 @@
32
32
  <div class="text-muted"><%= batch.description %></div>
33
33
  <% end %>
34
34
  </div>
35
- <div class="col-md-1 text-wrap">
36
- <div class="d-md-none small text-muted mt-1">Created at</div>
35
+ <div class="col-6 col-lg-1 text-wrap">
36
+ <div class="d-lg-none small text-muted mt-1">Created at</div>
37
37
  <%= relative_time(batch.created_at) %>
38
38
  </div>
39
- <div class="col-md-1 text-wrap">
39
+ <div class="col-6 col-lg-1 text-wrap">
40
40
  <% if batch.enqueued_at %>
41
- <div class="d-md-none small text-muted mt-1">Enqueued at</div>
41
+ <div class="d-lg-none small text-muted mt-1">Enqueued at</div>
42
42
  <%= relative_time(batch.enqueued_at) %>
43
43
  <% end %>
44
44
  </div>
45
- <div class="col-md-1 text-wrap">
45
+ <div class="col-6 col-lg-1 text-wrap">
46
46
  <% if batch.discarded_at %>
47
- <div class="d-md-none small text-muted mt-1">Discarded at</div>
47
+ <div class="d-lg-none small text-muted mt-1">Discarded at</div>
48
48
  <%= relative_time(batch.discarded_at) %>
49
49
  <% end %>
50
50
  </div>
51
- <div class="col-md-1 text-wrap">
51
+ <div class="col-6 col-lg-1 text-wrap">
52
52
  <% if batch.finished_at %>
53
- <div class="d-md-none small text-muted mt-1">Finished at</div>
53
+ <div class="d-lg-none small text-muted mt-1">Finished at</div>
54
54
  <%= relative_time(batch.finished_at) %>
55
55
  <% end %>
56
56
  </div>
57
- <div class="col">
58
- <div class="d-md-none small text-muted mt-1">Jobs</div>
57
+ <div class="col-6 col-lg-1">
58
+ <div class="d-lg-none small text-muted mt-1">Jobs</div>
59
59
  <%= batch.jobs.count %>
60
60
  </div>
61
61
  <div class="col text-end">
62
- <%= tag.button type: "button", class: "btn btn-sm text-muted ms-auto", role: "button",
62
+ <%= tag.button type: "button", class: "btn btn-sm text-muted", role: "button",
63
63
  title: "Inspect",
64
64
  data: { bs_toggle: "collapse", bs_target: "##{dom_id(batch, 'properties')}" },
65
65
  aria: { expanded: false, controls: dom_id(batch, "state") } do %>
@@ -6,11 +6,11 @@
6
6
  <div class="list-group list-group-flush text-nowrap" role="table">
7
7
  <header class="list-group-item bg-light">
8
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>
9
+ <div class="col-12 col-lg-2"></div>
10
+ <div class="col-6 col-lg-2 d-none d-lg-block">Class</div>
11
+ <div class="col-6 col-lg-2 d-none d-lg-block">Schedule</div>
12
+ <div class="col-6 col-lg-2 d-none d-lg-block">Next scheduled</div>
13
+ <div class="col-6 col-lg-2 d-none d-lg-block">Last run</div>
14
14
  <div class="col text-end">
15
15
  <%= tag.button type: "button", class: "btn btn-sm text-muted", role: "button",
16
16
  data: { bs_toggle: "collapse", bs_target: ".cron-entry-properties" },
@@ -24,15 +24,22 @@
24
24
  <% @cron_entries.each do |cron_entry| %>
25
25
  <div id="<%= dom_id(cron_entry) %>" class="list-group-item py-3" role="row">
26
26
  <div class="row align-items-center">
27
- <div class="col">
27
+ <div class="col-12 col-lg-2">
28
28
  <div class="small font-monospace"><%= cron_entry.key %></div>
29
- <div class="small text-muted"><%= cron_entry.description %></div>
29
+ <div class="small text-muted text-wrap"><%= cron_entry.description %></div>
30
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">
31
+ <div class="col-12 col-lg-2 text-wrap"><%= tag.span tag.code(cron_entry.job_class), class: "fs-5 mb-0" %></div>
32
+ <div class="col-6 col-lg-2 text-wrap">
33
+ <div class="d-lg-none small text-muted mt-1">Schedule</div>
34
+ <span class="font-monospace fw-bold"><%= cron_entry.schedule %></span>
35
+ </div>
36
+ <div class="col-6 col-lg-2 text-wrap small">
37
+ <div class="d-lg-none small text-muted mt-1">Next scheduled</div>
38
+ <%= relative_time cron_entry.next_at %>
39
+ </div>
40
+ <div class="col-6 col-lg-2 text-wrap small">
35
41
  <% if cron_entry.last_job.present? %>
42
+ <div class="d-lg-none small text-muted mt-1">Last run</div>
36
43
  <%= link_to relative_time(cron_entry.last_at), cron_entry_path(cron_entry), title: "Job #{cron_entry.last_job.id}" %>
37
44
  <% end %>
38
45
  </div>
@@ -23,7 +23,7 @@
23
23
  <%= tag.span relative_time(execution.last_status_at, include_seconds: true), class: "small" %>
24
24
  <%= status_badge execution.status %>
25
25
 
26
- <%= tag.button type: "button", class: "btn btn-sm text-muted ms-auto", role: "button",
26
+ <%= tag.button type: "button", class: "btn btn-sm text-muted", role: "button",
27
27
  title: "Inspect",
28
28
  data: { bs_toggle: "collapse", bs_target: "##{dom_id(execution, 'params')}" },
29
29
  aria: { expanded: false, controls: dom_id(execution, "params") } do %>
@@ -3,12 +3,11 @@
3
3
  <div class="list-group list-group-flush text-nowrap table-jobs" role="table">
4
4
  <header class="list-group-item bg-light">
5
5
  <div class="row small text-muted text-uppercase align-items-center">
6
- <div class="col-auto">
6
+ <div class="col-lg-4 d-flex gap-2 flex-wrap">
7
7
  <%= label_tag('toggle_job_ids', "Toggle all jobs", class: "visually-hidden") %>
8
8
  <%= check_box_tag('toggle_job_ids', "1", false, data: { "checkbox-toggle-all": "job_ids" }) %>
9
- </div>
10
- <div class="col-md-4">
11
- <%= 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 %>
9
+
10
+ <%= form.button type: 'submit', name: 'mass_action', value: 'reschedule', class: 'ms-1 btn btn-sm btn-outline-secondary', title: "Reschedule all", data: { confirm: "Are you sure you want to reschedule the selected jobs?", disable: true } do %>
12
11
  <span class="me-1"><%= render_icon "skip_forward" %></span> Reschedule
13
12
  <% end %>
14
13
 
@@ -33,9 +32,9 @@
33
32
  </div>
34
33
 
35
34
  </div>
36
- <div class="d-none d-md-block col-md-1">Queue</div>
37
- <div class="d-none d-md-block col-md-1">Priority</div>
38
- <div class="d-none d-md-block col-md-1 text-end">Attempts</div>
35
+ <div class="d-none d-lg-block col-lg-1 text-lg-center">Queue</div>
36
+ <div class="d-none d-lg-block col-lg-1 text-lg-end">Priority</div>
37
+ <div class="d-none d-lg-block col-lg-1 text-lg-end">Attempts</div>
39
38
  <div class="col text-end">
40
39
  <%= tag.button type: "button", class: "btn btn-sm text-muted", role: "button",
41
40
  data: { bs_toggle: "collapse", bs_target: ".job-params" },
@@ -61,21 +60,23 @@
61
60
  <% jobs.each do |job| %>
62
61
  <%= label_tag dom_id(job, :checkbox), id: dom_id(job), role: "row", class: "list-group-item list-group-item-action py-3" do %>
63
62
  <div class="row align-items-center">
64
- <div class="col-auto">
63
+ <div class="col-lg-4 d-flex">
65
64
  <%= check_box_tag 'job_ids[]', job.id, false, id: dom_id(job, :checkbox), data: { "checkbox-toggle-each": "job_ids" } %>
65
+ <div class="ms-2">
66
+ <%= tag.code link_to(job.id, job_path(job), class: "small text-muted text-decoration-none") %>
67
+ <%= tag.h5 tag.code(link_to(job.job_class, job_path(job), class: "text-reset text-decoration-none")), class: "text-reset mb-0" %>
68
+ </div>
66
69
  </div>
67
- <div class="col-md-4">
68
- <%= tag.code link_to(job.id, job_path(job), class: "small text-muted text-decoration-none") %>
69
- <%= tag.h5 tag.code(link_to(job.job_class, job_path(job), class: "text-reset text-decoration-none")), class: "text-reset mb-0" %>
70
- </div>
71
- <div class="col-md-1">
70
+ <div class="col-4 col-lg-1 text-lg-center">
71
+ <div class="d-lg-none small text-muted mt-1">Queue</div>
72
72
  <span class="badge bg-primary bg-opacity-25 text-dark font-monospace"><%= job.queue_name %></span>
73
73
  </div>
74
- <div class="col-md-1 small text-md-center">
75
- <span class="font-monospace fw-bold"><%= job.priority %></span>
76
- <span class="d-md-none">Priority</span>
74
+ <div class="col-4 col-lg-1 text-lg-end">
75
+ <div class="d-lg-none small text-muted mt-1">Priority</div>
76
+ <%= job.priority %>
77
77
  </div>
78
- <div class="col-md-1 text-md-center">
78
+ <div class="col-4 col-lg-1 text-lg-end">
79
+ <div class="d-lg-none small text-muted mt-1">Attempts</div>
79
80
  <% if job.executions_count > 0 && job.status != :succeeded %>
80
81
  <%= tag.span job.executions_count, class: "badge rounded-pill bg-danger", data: {
81
82
  bs_toggle: "popover",
@@ -86,56 +87,56 @@
86
87
  <% else %>
87
88
  <span class="badge bg-secondary bg-opacity-50 rounded-pill"><%= job.executions_count %></span>
88
89
  <% end %>
89
- <span class="d-md-none small">Attemp</span>
90
- </div>
91
- <div class="col d-flex gap-3 align-items-center justify-content-end">
92
- <%= tag.span relative_time(job.last_status_at), class: "small" %>
93
- <%= status_badge job.status %>
94
90
  </div>
95
- <div class="col-auto">
96
- <div class="dropdown float-end">
97
- <button class="d-flex align-items-center btn btn-sm" type="button" id="<%= dom_id(job, :actions) %>" data-bs-toggle="dropdown" aria-expanded="false">
98
- <%= render "good_job/shared/icons/dots" %>
99
- <span class="visually-hidden">Actions</span>
100
- </button>
101
- <ul class="dropdown-menu shadow" aria-labelledby="<%= dom_id(job, :actions) %>">
102
- <li>
103
- <% job_reschedulable = job.status.in? [:scheduled, :retried, :queued] %>
104
- <%= 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 %>
105
- <%= render "good_job/shared/icons/skip_forward" %>
106
- Reschedule
107
- <% end %>
108
- </li>
109
- <li>
110
- <% job_discardable = job.status.in? [:scheduled, :retried, :queued] %>
111
- <%= 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 %>
112
- <%= render "good_job/shared/icons/stop" %>
113
- Discard
114
- <% end %>
115
- </li>
116
- <li>
117
- <%= 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 %>
118
- <%= render "good_job/shared/icons/arrow_clockwise" %>
119
- Retry
120
- <% end %>
121
- </li>
122
- <li>
123
- <%= link_to job_path(job.id), method: :delete, class: "dropdown-item #{'disabled' unless job.status.in? [:discarded, :succeeded]}", title: "Destroy job", data: { confirm: "Confirm destroy", disable: true } do %>
124
- <%= render_icon "trash" %>
125
- Destroy
126
- <% end %>
127
- </li>
91
+ <div class="mt-3 mt-lg-0 col">
92
+ <div class="d-flex gap-3 align-items-center justify-content-end">
93
+ <%= tag.span relative_time(job.last_status_at), class: "small" %>
94
+ <%= status_badge job.status %>
95
+
96
+ <div class="dropdown float-end">
97
+ <button class="d-flex align-items-center btn btn-sm" type="button" id="<%= dom_id(job, :actions) %>" data-bs-toggle="dropdown" aria-expanded="false">
98
+ <%= render "good_job/shared/icons/dots" %>
99
+ <span class="visually-hidden">Actions</span>
100
+ </button>
101
+ <ul class="dropdown-menu shadow" aria-labelledby="<%= dom_id(job, :actions) %>">
102
+ <li>
103
+ <% job_reschedulable = job.status.in? [:scheduled, :retried, :queued] %>
104
+ <%= 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 %>
105
+ <%= render "good_job/shared/icons/skip_forward" %>
106
+ Reschedule
107
+ <% end %>
108
+ </li>
109
+ <li>
110
+ <% job_discardable = job.status.in? [:scheduled, :retried, :queued] %>
111
+ <%= 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 %>
112
+ <%= render "good_job/shared/icons/stop" %>
113
+ Discard
114
+ <% end %>
115
+ </li>
116
+ <li>
117
+ <%= 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 %>
118
+ <%= render "good_job/shared/icons/arrow_clockwise" %>
119
+ Retry
120
+ <% end %>
121
+ </li>
122
+ <li>
123
+ <%= link_to job_path(job.id), method: :delete, class: "dropdown-item #{'disabled' unless job.status.in? [:discarded, :succeeded]}", title: "Destroy job", data: { confirm: "Confirm destroy", disable: true } do %>
124
+ <%= render_icon "trash" %>
125
+ Destroy
126
+ <% end %>
127
+ </li>
128
128
 
129
- <li>
130
- <%= link_to "##{dom_id(job, 'params')}",
131
- class: "dropdown-item",
132
- data: { bs_toggle: "collapse" },
133
- aria: { expanded: false, controls: dom_id(job, "params") } do %>
134
- <%= render_icon "info" %>
135
- Inspect
136
- <% end %>
137
- </li>
138
- </ul>
129
+ <li>
130
+ <%= link_to "##{dom_id(job, 'params')}",
131
+ class: "dropdown-item",
132
+ data: { bs_toggle: "collapse" },
133
+ aria: { expanded: false, controls: dom_id(job, "params") } do %>
134
+ <%= render_icon "info" %>
135
+ Inspect
136
+ <% end %>
137
+ </li>
138
+ </ul>
139
+ </div>
139
140
  </div>
140
141
  </div>
141
142
  </div>
@@ -11,7 +11,7 @@
11
11
  <%= link_to jobs_path, class: ["nav-link", ("active" if controller_name == 'jobs')] do %>
12
12
  <%= t(".jobs") %>
13
13
  <% jobs_count = GoodJob::Job.count %>
14
- <span class="badge bg-secondary rounded-pill"><%= number_to_human(jobs_count) %></span>
14
+ <span class="badge bg-secondary rounded-pill"><%= number_to_human(jobs_count, unit: "good_job.number") %></span>
15
15
  <% end %>
16
16
  </li>
17
17
  <li class="nav-item">
@@ -1,51 +1,69 @@
1
1
  ---
2
2
  en:
3
- datetime:
4
- distance_in_words:
5
- about_x_hours:
6
- one: about 1 hour
7
- other: about %{count} hours
8
- about_x_months:
9
- one: about 1 month
10
- other: about %{count} months
11
- about_x_years:
12
- one: about 1 year
13
- other: about %{count} years
14
- almost_x_years:
15
- one: almost 1 year
16
- other: almost %{count} years
17
- half_a_minute: half a minute
18
- less_than_x_minutes:
19
- one: less than a minute
20
- other: less than %{count} minutes
21
- less_than_x_seconds:
22
- one: less than 1 second
23
- other: less than %{count} seconds
24
- over_x_years:
25
- one: over 1 year
26
- other: over %{count} years
27
- x_days:
28
- one: 1 day
29
- other: "%{count} days"
30
- x_minutes:
31
- one: 1 minute
32
- other: "%{count} minutes"
33
- x_months:
34
- one: 1 month
35
- other: "%{count} months"
36
- x_seconds:
37
- one: 1 second
38
- other: "%{count} seconds"
39
- x_years:
40
- one: 1 year
41
- other: "%{count} years"
42
- duration:
43
- hours: "%{hour}h %{min}m"
44
- less_than_10_seconds: "%{sec}s"
45
- milliseconds: "%{ms}ms"
46
- minutes: "%{min}m %{sec}s"
47
- seconds: "%{sec}s"
48
3
  good_job:
4
+ datetime:
5
+ distance_in_words:
6
+ about_x_hours:
7
+ one: about 1 hour
8
+ other: about %{count} hours
9
+ about_x_months:
10
+ one: about 1 month
11
+ other: about %{count} months
12
+ about_x_years:
13
+ one: about 1 year
14
+ other: about %{count} years
15
+ almost_x_years:
16
+ one: almost 1 year
17
+ other: almost %{count} years
18
+ half_a_minute: half a minute
19
+ less_than_x_minutes:
20
+ one: less than a minute
21
+ other: less than %{count} minutes
22
+ less_than_x_seconds:
23
+ one: less than 1 second
24
+ other: less than %{count} seconds
25
+ over_x_years:
26
+ one: over 1 year
27
+ other: over %{count} years
28
+ x_days:
29
+ one: 1 day
30
+ other: "%{count} days"
31
+ x_minutes:
32
+ one: 1 minute
33
+ other: "%{count} minutes"
34
+ x_months:
35
+ one: 1 month
36
+ other: "%{count} months"
37
+ x_seconds:
38
+ one: 1 second
39
+ other: "%{count} seconds"
40
+ x_years:
41
+ one: 1 year
42
+ other: "%{count} years"
43
+ duration:
44
+ hours: "%{hour}h %{min}m"
45
+ less_than_10_seconds: "%{sec}s"
46
+ milliseconds: "%{ms}ms"
47
+ minutes: "%{min}m %{sec}s"
48
+ seconds: "%{sec}s"
49
+ number:
50
+ format:
51
+ delimiter: ","
52
+ separator: "."
53
+ human:
54
+ decimal_units:
55
+ format: "%n%u"
56
+ units:
57
+ billion: B
58
+ million: M
59
+ quadrillion: Q
60
+ thousand: K
61
+ trillion: T
62
+ unit: ''
63
+ format:
64
+ delimiter: ","
65
+ precision: 3
66
+ separator: "."
49
67
  shared:
50
68
  footer:
51
69
  last_update_html: Last updated <time id="page-updated-at" datetime="%{time}">%{time}</time>
@@ -63,21 +81,3 @@ en:
63
81
  running: Running
64
82
  scheduled: Scheduled
65
83
  succeeded: Succeeded
66
- number:
67
- format:
68
- delimiter: ","
69
- separator: "."
70
- human:
71
- decimal_units:
72
- format: "%n%u"
73
- units:
74
- billion: B
75
- million: M
76
- quadrillion: Q
77
- thousand: K
78
- trillion: T
79
- unit: ''
80
- format:
81
- delimiter: ","
82
- precision: 3
83
- separator: "."