good_job 4.19.2 → 4.19.4
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 +4 -4
- data/CHANGELOG.md +36 -0
- data/README.md +1 -1
- data/app/charts/good_job/performance_index_chart.rb +50 -17
- data/app/charts/good_job/performance_range.rb +380 -0
- data/app/charts/good_job/performance_show_chart.rb +4 -3
- data/app/charts/good_job/scheduled_by_queue_chart.rb +2 -2
- data/app/controllers/good_job/cleaner_controller.rb +1 -1
- data/app/controllers/good_job/performance_controller.rb +28 -3
- data/app/frontend/good_job/application.js +2 -0
- data/app/frontend/good_job/modules/chart_controller.js +252 -2
- data/app/frontend/good_job/modules/performance_range_controller.js +321 -0
- data/app/frontend/good_job/modules/timezone_name_formatter.js +13 -0
- data/app/frontend/good_job/style.css +97 -0
- data/app/helpers/good_job/application_helper.rb +16 -0
- data/app/models/good_job/cron_entry.rb +34 -4
- data/app/models/good_job/setting.rb +16 -4
- data/app/views/good_job/batches/show.html.erb +1 -1
- data/app/views/good_job/performance/_range.erb +91 -0
- data/app/views/good_job/performance/index.html.erb +17 -5
- data/app/views/good_job/performance/show.html.erb +4 -2
- data/config/locales/az.yml +321 -0
- data/config/locales/de.yml +24 -0
- data/config/locales/en.yml +24 -0
- data/config/locales/es.yml +24 -0
- data/config/locales/fr.yml +24 -0
- data/config/locales/it.yml +24 -0
- data/config/locales/ja.yml +24 -0
- data/config/locales/ko.yml +24 -0
- data/config/locales/nl.yml +24 -0
- data/config/locales/pt-BR.yml +24 -0
- data/config/locales/ru.yml +24 -0
- data/config/locales/tr.yml +24 -0
- data/config/locales/uk.yml +24 -0
- data/config/locales/zh-CN.yml +24 -0
- data/lib/generators/good_job/templates/install/migrations/create_good_jobs.rb.erb +3 -0
- data/lib/generators/good_job/templates/update/migrations/15_add_index_good_jobs_discarded_job_class.rb.erb +13 -0
- data/lib/good_job/adapter.rb +12 -7
- data/lib/good_job/configuration/validator.rb +42 -0
- data/lib/good_job/configuration.rb +12 -0
- data/lib/good_job/probe_server.rb +12 -2
- data/lib/good_job/version.rb +1 -1
- data/lib/good_job.rb +3 -3
- metadata +9 -2
|
@@ -3,6 +3,103 @@
|
|
|
3
3
|
height: 200px;
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
+
.chart-range-selection {
|
|
7
|
+
position: absolute;
|
|
8
|
+
pointer-events: none;
|
|
9
|
+
background-color: rgba(var(--bs-primary-rgb), 0.2);
|
|
10
|
+
border: 1px solid rgba(var(--bs-primary-rgb), 0.75);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.performance-range-control {
|
|
14
|
+
align-items: stretch;
|
|
15
|
+
display: flex;
|
|
16
|
+
flex-wrap: wrap;
|
|
17
|
+
gap: 0.25rem;
|
|
18
|
+
justify-content: flex-start;
|
|
19
|
+
max-width: 100%;
|
|
20
|
+
width: 100%;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.performance-range-state,
|
|
24
|
+
.performance-range-control .btn {
|
|
25
|
+
align-items: center;
|
|
26
|
+
display: inline-flex;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.performance-range-state {
|
|
30
|
+
background-color: var(--bs-body-bg);
|
|
31
|
+
border: var(--bs-border-width) solid var(--bs-border-color);
|
|
32
|
+
border-radius: var(--bs-border-radius);
|
|
33
|
+
color: var(--bs-body-color);
|
|
34
|
+
font-size: 1rem;
|
|
35
|
+
line-height: 1.5;
|
|
36
|
+
padding: 0.375rem 0.75rem;
|
|
37
|
+
white-space: nowrap;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.performance-range-date {
|
|
41
|
+
cursor: pointer;
|
|
42
|
+
gap: 0.25rem;
|
|
43
|
+
position: relative;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.performance-range-native-input {
|
|
47
|
+
cursor: pointer;
|
|
48
|
+
height: 100%;
|
|
49
|
+
inset: 0;
|
|
50
|
+
opacity: 0;
|
|
51
|
+
position: absolute;
|
|
52
|
+
width: 100%;
|
|
53
|
+
z-index: 1;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.performance-range-date:focus-within {
|
|
57
|
+
border-color: var(--bs-primary);
|
|
58
|
+
box-shadow: 0 0 0 0.125rem var(--bs-body-bg);
|
|
59
|
+
outline: 0.1875rem solid var(--bs-primary);
|
|
60
|
+
outline-offset: 0.125rem;
|
|
61
|
+
z-index: 2;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.performance-range-date:has(.performance-range-native-input:invalid) {
|
|
65
|
+
border-color: var(--bs-danger);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.performance-range-toggle {
|
|
69
|
+
margin-left: calc(-1 * var(--bs-border-width));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.performance-range-toggle:focus-visible {
|
|
73
|
+
border-color: var(--bs-primary);
|
|
74
|
+
box-shadow: 0 0 0 0.125rem var(--bs-body-bg);
|
|
75
|
+
outline: 0.1875rem solid var(--bs-primary);
|
|
76
|
+
outline-offset: 0.125rem;
|
|
77
|
+
position: relative;
|
|
78
|
+
z-index: 2;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.performance-range-key,
|
|
82
|
+
.performance-range-option-key {
|
|
83
|
+
justify-content: center;
|
|
84
|
+
min-width: 2.5rem;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.performance-range-menu {
|
|
88
|
+
min-width: 14rem;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.performance-name {
|
|
92
|
+
overflow-wrap: anywhere;
|
|
93
|
+
white-space: normal;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@media (max-width: 575.98px) {
|
|
97
|
+
.performance-range-control {
|
|
98
|
+
gap: 0.125rem;
|
|
99
|
+
width: 100%;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
6
103
|
.legend-item-color-box {
|
|
7
104
|
display: inline-block;
|
|
8
105
|
flex-shrink: 0;
|
|
@@ -34,6 +34,22 @@ module GoodJob
|
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
+
def format_performance_bucket_size(seconds)
|
|
38
|
+
if seconds >= 365.days && (seconds % 365.days.to_i).zero?
|
|
39
|
+
t "good_job.performance.range.bucket_size.years", count: seconds / 365.days.to_i
|
|
40
|
+
elsif seconds >= 30.days && (seconds % 30.days.to_i).zero?
|
|
41
|
+
t "good_job.performance.range.bucket_size.months", count: seconds / 30.days.to_i
|
|
42
|
+
elsif seconds >= 1.day && (seconds % 1.day.to_i).zero?
|
|
43
|
+
t "good_job.performance.range.bucket_size.days", count: seconds / 1.day.to_i
|
|
44
|
+
elsif (seconds % 1.hour.to_i).zero?
|
|
45
|
+
t "good_job.performance.range.bucket_size.hours", count: seconds / 1.hour.to_i
|
|
46
|
+
elsif (seconds % 1.minute.to_i).zero?
|
|
47
|
+
t "good_job.performance.range.bucket_size.minutes", count: seconds / 1.minute.to_i
|
|
48
|
+
else
|
|
49
|
+
t "good_job.performance.range.bucket_size.seconds", count: seconds
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
37
53
|
def relative_time(timestamp, **options)
|
|
38
54
|
options = options.reverse_merge({ scope: "good_job.datetime.distance_in_words" })
|
|
39
55
|
text = t("good_job.helpers.relative_time.#{timestamp.future? ? 'future' : 'past'}", time: time_ago_in_words(timestamp, **options))
|
|
@@ -13,6 +13,15 @@ module GoodJob # :nodoc:
|
|
|
13
13
|
|
|
14
14
|
attr_reader :params
|
|
15
15
|
|
|
16
|
+
VALUE_TYPES = {
|
|
17
|
+
args: Array,
|
|
18
|
+
kwargs: Hash,
|
|
19
|
+
set: Hash,
|
|
20
|
+
}.freeze
|
|
21
|
+
|
|
22
|
+
validate :validate_job_class
|
|
23
|
+
validate :validate_value_types
|
|
24
|
+
|
|
16
25
|
def self.all(configuration: nil)
|
|
17
26
|
configuration ||= GoodJob.configuration
|
|
18
27
|
configuration.cron_entries
|
|
@@ -28,8 +37,8 @@ module GoodJob # :nodoc:
|
|
|
28
37
|
|
|
29
38
|
join_clause = <<~SQL.squish
|
|
30
39
|
CROSS JOIN LATERAL (
|
|
31
|
-
SELECT * FROM
|
|
32
|
-
WHERE
|
|
40
|
+
SELECT * FROM #{GoodJob::Job.quoted_table_name}
|
|
41
|
+
WHERE #{GoodJob::Job.quoted_table_name}.cron_key = cron_keys.cron_key
|
|
33
42
|
ORDER BY cron_at DESC NULLS LAST
|
|
34
43
|
LIMIT 1
|
|
35
44
|
) AS lateral_jobs
|
|
@@ -126,8 +135,7 @@ module GoodJob # :nodoc:
|
|
|
126
135
|
current_thread.cron_at = cron_at
|
|
127
136
|
|
|
128
137
|
I18n.with_locale(I18n.default_locale) do
|
|
129
|
-
job_klass =
|
|
130
|
-
job_klass = job_klass.constantize if job_klass.is_a?(String)
|
|
138
|
+
job_klass = resolve_job_class
|
|
131
139
|
next unless job_klass.is_a?(Class)
|
|
132
140
|
|
|
133
141
|
configured_job = job_klass.set(set_value)
|
|
@@ -175,6 +183,28 @@ module GoodJob # :nodoc:
|
|
|
175
183
|
|
|
176
184
|
private
|
|
177
185
|
|
|
186
|
+
def validate_job_class
|
|
187
|
+
return if job_class.blank? || job_class.is_a?(Class) || job_class.respond_to?(:call)
|
|
188
|
+
|
|
189
|
+
resolve_job_class
|
|
190
|
+
rescue NameError
|
|
191
|
+
errors.add(:class, "'#{job_class}' does not exist")
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def validate_value_types
|
|
195
|
+
VALUE_TYPES.each do |attribute, type|
|
|
196
|
+
value = public_send(attribute)
|
|
197
|
+
next if value.blank? || value.respond_to?(:call) || value.is_a?(type)
|
|
198
|
+
|
|
199
|
+
errors.add(attribute, "must be a #{type}")
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def resolve_job_class
|
|
204
|
+
value = job_class_value
|
|
205
|
+
value.is_a?(String) ? value.constantize : value
|
|
206
|
+
end
|
|
207
|
+
|
|
178
208
|
def cron
|
|
179
209
|
params.fetch(:cron)
|
|
180
210
|
end
|
|
@@ -101,10 +101,22 @@ module GoodJob
|
|
|
101
101
|
setting.save!
|
|
102
102
|
end
|
|
103
103
|
|
|
104
|
-
def self.paused?(queue: nil, job_class: nil, label: nil)
|
|
105
|
-
raise ArgumentError, "Must provide at most one of queue, job_class, or label" if [queue, job_class, label].many?(&:present?)
|
|
104
|
+
def self.paused?(active_job: nil, queue: nil, job_class: nil, label: nil)
|
|
105
|
+
raise ArgumentError, "Must provide at most one of active_job, queue, job_class, or label" if [active_job, queue, job_class, label].many?(&:present?)
|
|
106
106
|
|
|
107
|
-
if
|
|
107
|
+
if active_job
|
|
108
|
+
pauses = paused
|
|
109
|
+
|
|
110
|
+
return :queue_name_paused if pauses[:queues].include?(active_job.queue_name.to_s)
|
|
111
|
+
return :job_class_paused if pauses[:job_classes].include?(active_job.class.to_s)
|
|
112
|
+
|
|
113
|
+
labels = active_job.respond_to?(:good_job_labels) ? Array(active_job.good_job_labels) : []
|
|
114
|
+
labels = labels.filter_map { |label| label.to_s.presence }.uniq
|
|
115
|
+
|
|
116
|
+
return :label_paused if (labels & pauses[:labels]).any?
|
|
117
|
+
|
|
118
|
+
false
|
|
119
|
+
elsif queue
|
|
108
120
|
queue.in? paused(:queues)
|
|
109
121
|
elsif job_class
|
|
110
122
|
job_class.in? paused(:job_classes)
|
|
@@ -116,7 +128,7 @@ module GoodJob
|
|
|
116
128
|
end
|
|
117
129
|
|
|
118
130
|
def self.paused(type = nil)
|
|
119
|
-
setting = find_by(key: PAUSES)
|
|
131
|
+
setting = uncached { find_by(key: PAUSES) }
|
|
120
132
|
pauses = setting&.value&.deep_dup || { "queues" => [], "job_classes" => [], "labels" => [] }
|
|
121
133
|
pauses = pauses.with_indifferent_access
|
|
122
134
|
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
<div class="my-4">
|
|
26
26
|
<h5><%= t ".attributes" %></h5>
|
|
27
27
|
<div class="bg-dark text-secondary p-3 rounded">
|
|
28
|
-
<%= tag.pre JSON.pretty_generate
|
|
28
|
+
<%= tag.pre JSON.pretty_generate(@batch.display_attributes), class: 'text-wrap text-break' %>
|
|
29
29
|
</div>
|
|
30
30
|
</div>
|
|
31
31
|
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
<% range_badge_label = performance_range.key || t(".custom_range") %>
|
|
2
|
+
<% reload_range_label = t(".reload_range") %>
|
|
3
|
+
<% reload_range_query = range_context.merge(performance_range.reload_params).to_query %>
|
|
4
|
+
<% reload_range_path = reload_range_query.present? ? "#{range_path}?#{reload_range_query}" : range_path %>
|
|
5
|
+
<% custom_range_path = "#{range_path}?#{range_context.merge(performance_range.custom_params).to_query}" %>
|
|
6
|
+
<% time_zone_label = Time.zone.name %>
|
|
7
|
+
<% range_form_data = {
|
|
8
|
+
action: "submit->performance-range#prepareSubmission",
|
|
9
|
+
controller: "performance-range",
|
|
10
|
+
performance_range_application_time_zone_value: time_zone_label,
|
|
11
|
+
performance_range_application_time_zone_identifier_value: Time.zone.tzinfo.name,
|
|
12
|
+
performance_range_end_fallback_value: performance_range.end_local_value,
|
|
13
|
+
performance_range_end_label_value: performance_range.end_label,
|
|
14
|
+
performance_range_end_timestamp_value: performance_range.canonical_timestamp(performance_range.end_time),
|
|
15
|
+
performance_range_label_style_value: performance_range.label_style,
|
|
16
|
+
performance_range_maximum_value: GoodJob::PerformanceRange::MAXIMUM_LOCAL_VALUE,
|
|
17
|
+
performance_range_minimum_value: GoodJob::PerformanceRange::MINIMUM_LOCAL_VALUE,
|
|
18
|
+
performance_range_start_fallback_value: performance_range.start_local_value,
|
|
19
|
+
performance_range_start_label_value: performance_range.start_label,
|
|
20
|
+
performance_range_start_timestamp_value: performance_range.canonical_timestamp(performance_range.start_time),
|
|
21
|
+
} %>
|
|
22
|
+
|
|
23
|
+
<%= tag.form(
|
|
24
|
+
action: range_path,
|
|
25
|
+
method: "get",
|
|
26
|
+
class: "performance-range-toolbar mb-3",
|
|
27
|
+
aria: { describedby: "performance-range-time-zone", labelledby: "performance-range-name" },
|
|
28
|
+
data: range_form_data
|
|
29
|
+
) do %>
|
|
30
|
+
<span class="visually-hidden" id="performance-range-name"><%= t ".time_range" %></span>
|
|
31
|
+
<% if range_context["locale"] %>
|
|
32
|
+
<%= hidden_field_tag "locale", range_context["locale"] %>
|
|
33
|
+
<% end %>
|
|
34
|
+
<%= hidden_field_tag "chart_start", performance_range.canonical_timestamp(performance_range.start_time), disabled: true, id: nil, data: { performance_range_target: "startCanonicalInput" } %>
|
|
35
|
+
<%= hidden_field_tag "chart_end", performance_range.canonical_timestamp(performance_range.end_time), disabled: true, id: nil, data: { performance_range_target: "endCanonicalInput" } %>
|
|
36
|
+
<%= hidden_field_tag GoodJob::PerformanceRange::TIME_ZONE_PARAMETER_KEY, nil, disabled: true, id: nil, data: { performance_range_target: "timeZoneInput" } %>
|
|
37
|
+
<div class="performance-range-control">
|
|
38
|
+
<label class="performance-range-state performance-range-date" title="<%= t ".start_time" %>">
|
|
39
|
+
<%= render_icon "clock", class: "me-1" %>
|
|
40
|
+
<span data-performance-range-target="startLabel"><%= performance_range.start_label %></span>
|
|
41
|
+
<%= datetime_local_field_tag "chart_start", performance_range.start_local_value, min: GoodJob::PerformanceRange::MINIMUM_LOCAL_VALUE, max: GoodJob::PerformanceRange::MAXIMUM_LOCAL_VALUE, step: 1, required: true, class: "performance-range-native-input", aria: { describedby: "performance-range-time-zone", label: t(".start_time") }, data: { performance_range_target: "startInput", action: "input->performance-range#edit input->performance-range#constrain change->performance-range#constrain change->performance-range#submitRange click->performance-range#openPicker" } %>
|
|
42
|
+
</label>
|
|
43
|
+
<label class="performance-range-state performance-range-date" title="<%= t ".end_time" %>">
|
|
44
|
+
<span data-performance-range-target="endLabel"><%= performance_range.end_label %></span>
|
|
45
|
+
<%= datetime_local_field_tag "chart_end", performance_range.end_local_value, min: GoodJob::PerformanceRange::MINIMUM_LOCAL_VALUE, max: GoodJob::PerformanceRange::MAXIMUM_LOCAL_VALUE, step: 1, required: true, class: "performance-range-native-input", aria: { describedby: "performance-range-time-zone", label: t(".end_time") }, data: { performance_range_target: "endInput", action: "input->performance-range#edit input->performance-range#constrain change->performance-range#constrain change->performance-range#submitRange click->performance-range#openPicker" } %>
|
|
46
|
+
</label>
|
|
47
|
+
<div class="performance-range-selector d-inline-flex position-relative">
|
|
48
|
+
<span class="performance-range-state performance-range-key rounded-end-0">
|
|
49
|
+
<%= range_badge_label %>
|
|
50
|
+
</span>
|
|
51
|
+
<button class="btn btn-outline-secondary dropdown-toggle dropdown-toggle-split performance-range-toggle rounded-start-0" type="button" data-bs-toggle="dropdown" aria-expanded="false" aria-label="<%= t ".open_time_ranges" %>">
|
|
52
|
+
<span class="visually-hidden"><%= t ".open_time_ranges" %></span>
|
|
53
|
+
</button>
|
|
54
|
+
<ul class="dropdown-menu dropdown-menu-end performance-range-menu">
|
|
55
|
+
<li><h6 class="dropdown-header"><%= t ".quick_ranges" %></h6></li>
|
|
56
|
+
<% performance_range.options.each do |range| %>
|
|
57
|
+
<% active = performance_range.key == range[:key] %>
|
|
58
|
+
<% preset_range_path = "#{range_path}?#{range_context.merge('chart_range' => range[:key]).to_query}" %>
|
|
59
|
+
<li>
|
|
60
|
+
<%= link_to preset_range_path, class: ["dropdown-item", "performance-range-menu-item", "d-flex", "align-items-center", "gap-2", ("active" if active)], aria: { current: ("page" if active) } do %>
|
|
61
|
+
<span class="performance-range-option-key"><%= range[:label] %></span>
|
|
62
|
+
<span class="flex-grow-1"><%= t ".range_options.#{range[:key]}" %></span>
|
|
63
|
+
<% end %>
|
|
64
|
+
</li>
|
|
65
|
+
<% end %>
|
|
66
|
+
<li><hr class="dropdown-divider"></li>
|
|
67
|
+
<li>
|
|
68
|
+
<%= link_to custom_range_path, class: ["dropdown-item", "performance-range-menu-item", "d-flex", "align-items-center", "gap-2", "performance-range-custom", ("active" if performance_range.custom?)], aria: { current: ("page" if performance_range.custom?) } do %>
|
|
69
|
+
<span class="performance-range-option-key">#</span>
|
|
70
|
+
<span class="flex-grow-1"><%= t ".custom_range" %></span>
|
|
71
|
+
<% if local_assigns[:drag_enabled] %>
|
|
72
|
+
<small class="text-body-secondary text-nowrap"><%= t ".custom_range_hint" %></small>
|
|
73
|
+
<% end %>
|
|
74
|
+
<% end %>
|
|
75
|
+
</li>
|
|
76
|
+
</ul>
|
|
77
|
+
</div>
|
|
78
|
+
<%= link_to reload_range_path, class: %w[btn btn-outline-secondary performance-range-reload flex-shrink-0], aria: { label: reload_range_label }, data: { turbo: false }, title: reload_range_label do %>
|
|
79
|
+
<%= render_icon "arrow_clockwise" %>
|
|
80
|
+
<span class="visually-hidden"><%= reload_range_label %></span>
|
|
81
|
+
<% end %>
|
|
82
|
+
</div>
|
|
83
|
+
<div class="performance-range-meta small text-body-secondary mt-2" id="performance-range-time-zone">
|
|
84
|
+
<span><%= t ".time_zone" %></span>
|
|
85
|
+
<span data-performance-range-target="timeZoneLabel"><%= time_zone_label %></span>
|
|
86
|
+
<% if local_assigns[:bucket_size] %>
|
|
87
|
+
<span class="mx-1" aria-hidden="true">·</span>
|
|
88
|
+
<span class="performance-chart-bucket-size"><%= t ".chart_bucket_size", size: bucket_size %></span>
|
|
89
|
+
<% end %>
|
|
90
|
+
</div>
|
|
91
|
+
<% end %>
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
<div class="border-bottom">
|
|
1
|
+
<div class="border-bottom mb-3">
|
|
2
2
|
<h2 class="pt-3 pb-2"><%= t ".title" %></h2>
|
|
3
3
|
</div>
|
|
4
4
|
|
|
5
|
-
<%= render
|
|
5
|
+
<%= render "range", performance_range: @performance_range, drag_enabled: true, range_context: @performance_range_context, range_path: performance_index_path(locale: nil), bucket_size: format_performance_bucket_size(@performance_range.interval_seconds) %>
|
|
6
|
+
|
|
7
|
+
<%= render 'good_job/shared/chart_container', chart_data: @chart_data %>
|
|
8
|
+
|
|
9
|
+
<% performance_range_query = @performance_range_context.merge(@performance_range.navigation_params).to_query %>
|
|
6
10
|
|
|
7
11
|
<div class="my-3 card">
|
|
8
12
|
<div class="list-group list-group-flush text-nowrap" role="table">
|
|
@@ -16,12 +20,16 @@
|
|
|
16
20
|
</div>
|
|
17
21
|
</header>
|
|
18
22
|
|
|
23
|
+
<% if @performances.empty? %>
|
|
24
|
+
<div role="row" class="list-group-item py-3 text-body-secondary"><%= t ".no_executions" %></div>
|
|
25
|
+
<% end %>
|
|
26
|
+
|
|
19
27
|
<% @performances.each do |performance| %>
|
|
20
28
|
<div role="row" class="list-group-item py-3">
|
|
21
29
|
<div class="row align-items-center">
|
|
22
|
-
<div class="col-12 col-lg-4">
|
|
30
|
+
<div class="col-12 col-lg-4 performance-name">
|
|
23
31
|
<div class="d-flex gap-3 justify-content-between">
|
|
24
|
-
<%= link_to performance.job_class, performance_path(performance.job_class) %>
|
|
32
|
+
<%= link_to performance.job_class, "#{performance_path(performance.job_class, locale: nil)}?#{performance_range_query}" %>
|
|
25
33
|
</div>
|
|
26
34
|
</div>
|
|
27
35
|
<div class="col-6 col-lg-2 text-wrap">
|
|
@@ -58,10 +66,14 @@
|
|
|
58
66
|
</div>
|
|
59
67
|
</header>
|
|
60
68
|
|
|
69
|
+
<% if @queue_performances.empty? %>
|
|
70
|
+
<div role="row" class="list-group-item py-3 text-body-secondary"><%= t ".no_executions" %></div>
|
|
71
|
+
<% end %>
|
|
72
|
+
|
|
61
73
|
<% @queue_performances.each do |performance| %>
|
|
62
74
|
<div role="row" class="list-group-item py-3">
|
|
63
75
|
<div class="row align-items-center">
|
|
64
|
-
<div class="col-12 col-lg-4">
|
|
76
|
+
<div class="col-12 col-lg-4 performance-name">
|
|
65
77
|
<div class="d-flex gap-3 justify-content-between">
|
|
66
78
|
<%= performance.queue_name %>
|
|
67
79
|
</div>
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
<div class="border-bottom">
|
|
1
|
+
<div class="border-bottom mb-3">
|
|
2
2
|
<h2 class="pt-3 pb-2"><%= t ".title" %> - <%= @job_class %></h2>
|
|
3
3
|
</div>
|
|
4
4
|
|
|
5
|
-
<%= render
|
|
5
|
+
<%= render "range", performance_range: @performance_range, range_context: @performance_range_context, range_path: performance_path(@job_class, locale: nil) %>
|
|
6
|
+
|
|
7
|
+
<%= render 'good_job/shared/chart_container', chart_data: @chart_data %>
|