jobs_dashboard 0.3.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/gem-push.yml +34 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +60 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +129 -0
- data/LICENSE.txt +21 -0
- data/README.md +133 -0
- data/Rakefile +8 -0
- data/app/assets/stylesheets/jobs_dashboard/application.scss +214 -0
- data/app/assets/stylesheets/jobs_dashboard/modules/buttons.sass +15 -0
- data/app/assets/stylesheets/jobs_dashboard/modules/card.sass +22 -0
- data/app/assets/stylesheets/jobs_dashboard/modules/description.sass +17 -0
- data/app/assets/stylesheets/jobs_dashboard/modules/grid.sass +17 -0
- data/app/assets/stylesheets/jobs_dashboard/modules/inputs.sass +31 -0
- data/app/assets/stylesheets/jobs_dashboard/modules/item-list.sass +10 -0
- data/app/assets/stylesheets/jobs_dashboard/modules/labels.sass +12 -0
- data/app/assets/stylesheets/jobs_dashboard/modules/statistics.sass +15 -0
- data/app/assets/stylesheets/jobs_dashboard/modules/table.sass +38 -0
- data/app/assets/stylesheets/jobs_dashboard/pages/jobs_logs/show.sass +36 -0
- data/app/assets/stylesheets/jobs_dashboard/reset.scss +135 -0
- data/app/assets/stylesheets/jobs_dashboard/variables.sass +6 -0
- data/app/controllers/jobs_dashboard/application_controller.rb +20 -0
- data/app/controllers/jobs_dashboard/dashboard_controller.rb +12 -0
- data/app/controllers/jobs_dashboard/job_logs_controller.rb +17 -0
- data/app/helpers/jobs_dashboard/application_helper.rb +31 -0
- data/app/models/jobs_dashboard/application_record.rb +5 -0
- data/app/models/jobs_dashboard/job_log.rb +32 -0
- data/app/views/jobs_dashboard/dashboard/_job_row.slim +8 -0
- data/app/views/jobs_dashboard/dashboard/index.slim +40 -0
- data/app/views/jobs_dashboard/job_logs/_stats.html.erb +0 -0
- data/app/views/jobs_dashboard/job_logs/index.slim +84 -0
- data/app/views/jobs_dashboard/job_logs/show.slim +89 -0
- data/app/views/kaminari/jobs_dashboard/_first_page.html.erb +11 -0
- data/app/views/kaminari/jobs_dashboard/_gap.html.erb +8 -0
- data/app/views/kaminari/jobs_dashboard/_last_page.html.erb +11 -0
- data/app/views/kaminari/jobs_dashboard/_next_page.html.erb +11 -0
- data/app/views/kaminari/jobs_dashboard/_page.html.erb +12 -0
- data/app/views/kaminari/jobs_dashboard/_paginator.html.erb +25 -0
- data/app/views/kaminari/jobs_dashboard/_prev_page.html.erb +11 -0
- data/app/views/layouts/jobs_dashboard/application.html.erb +15 -0
- data/app/views/shared/_logo.slim +4 -0
- data/app/views/shared/_navigation.slim +12 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/config/initializers/ransack.rb +11 -0
- data/config/locales/default.fr.yml +219 -0
- data/config/locales/jobs_dashboard.en.yml +33 -0
- data/config/locales/jobs_dashboard.fr.yml +37 -0
- data/config/routes.rb +5 -0
- data/jobs_dashboard.gemspec +38 -0
- data/lib/generators/active_record/job_log_migration_generator.rb +24 -0
- data/lib/generators/active_record/templates/migration.rb +20 -0
- data/lib/jobs_dashboard/client_middleware.rb +56 -0
- data/lib/jobs_dashboard/engine.rb +18 -0
- data/lib/jobs_dashboard/server_middleware.rb +62 -0
- data/lib/jobs_dashboard/storage.rb +54 -0
- data/lib/jobs_dashboard/version.rb +5 -0
- data/lib/jobs_dashboard/worker.rb +25 -0
- data/lib/jobs_dashboard.rb +12 -0
- metadata +161 -0
@@ -0,0 +1,31 @@
|
|
1
|
+
.form-inline
|
2
|
+
display: flex
|
3
|
+
gap: 16px
|
4
|
+
flex-wrap: wrap
|
5
|
+
align-items: center
|
6
|
+
&.submit
|
7
|
+
margin-top: 1rem
|
8
|
+
justify-content: flex-end
|
9
|
+
align-items: baseline
|
10
|
+
|
11
|
+
> .input
|
12
|
+
label
|
13
|
+
display: block
|
14
|
+
font-weight: 600
|
15
|
+
line-height: 2rem
|
16
|
+
color: #343434
|
17
|
+
input, select
|
18
|
+
min-width: 150px
|
19
|
+
&.group
|
20
|
+
display: flex
|
21
|
+
flex-direction: column
|
22
|
+
|
23
|
+
input, select
|
24
|
+
border-radius: 0.375rem
|
25
|
+
padding: 0px 12px
|
26
|
+
border: 1px solid #6b7280
|
27
|
+
box-shadow: none
|
28
|
+
font-size: 1rem
|
29
|
+
line-height: 1.5rem
|
30
|
+
height: 37px
|
31
|
+
outline-color: #A63446
|
@@ -0,0 +1,15 @@
|
|
1
|
+
@import "../variables"
|
2
|
+
.statistics
|
3
|
+
display: flex
|
4
|
+
justify-content: space-around
|
5
|
+
.stat
|
6
|
+
text-align: center
|
7
|
+
strong
|
8
|
+
font-size: 24px
|
9
|
+
margin-bottom: 1rem
|
10
|
+
justify-content: center
|
11
|
+
display: flex
|
12
|
+
color: $primary-color
|
13
|
+
font-weight: 600
|
14
|
+
font-size: 14px
|
15
|
+
color: $jet
|
@@ -0,0 +1,38 @@
|
|
1
|
+
$header-color: #f5f5f5
|
2
|
+
$table-radius: 6px
|
3
|
+
$cell-padding: 14px 10px
|
4
|
+
$table-border-color: #e4e4e4
|
5
|
+
|
6
|
+
table
|
7
|
+
border-radius: $table-radius
|
8
|
+
border: 1px solid $table-border-color
|
9
|
+
border-spacing: 0
|
10
|
+
border-collapse: separate
|
11
|
+
|
12
|
+
> thead
|
13
|
+
> tr
|
14
|
+
> th
|
15
|
+
border: 0
|
16
|
+
border-bottom: 1px solid $table-border-color
|
17
|
+
font-weight: 600
|
18
|
+
background-color: $header-color
|
19
|
+
color: darken($header-color, 40%)
|
20
|
+
padding: $cell-padding
|
21
|
+
|
22
|
+
&:first-child
|
23
|
+
border-top-left-radius: $table-radius
|
24
|
+
&:last-child
|
25
|
+
border-top-right-radius: $table-radius
|
26
|
+
> tbody
|
27
|
+
> tr
|
28
|
+
> td
|
29
|
+
border: 0
|
30
|
+
border-bottom: 1px solid $table-border-color
|
31
|
+
padding: $cell-padding
|
32
|
+
&:first-child
|
33
|
+
border-bottom-left-radius: $table-radius
|
34
|
+
&:last-child
|
35
|
+
border-bottom-right-radius: $table-radius
|
36
|
+
|
37
|
+
table tbody tr:hover
|
38
|
+
background-color: $header-color
|
@@ -0,0 +1,36 @@
|
|
1
|
+
.job_logs-show
|
2
|
+
.page-container
|
3
|
+
display: flex
|
4
|
+
flex-direction: column
|
5
|
+
width: 100%
|
6
|
+
|
7
|
+
.page-header
|
8
|
+
display: flex
|
9
|
+
flex-direction: column
|
10
|
+
gap: 20px
|
11
|
+
|
12
|
+
.title
|
13
|
+
font-size: 18px
|
14
|
+
font-weight: 600
|
15
|
+
|
16
|
+
.page-body
|
17
|
+
display: flex
|
18
|
+
flex-direction: row
|
19
|
+
gap: 20px
|
20
|
+
|
21
|
+
.main-container
|
22
|
+
flex: 8
|
23
|
+
display: flex
|
24
|
+
flex-direction: column
|
25
|
+
gap: 20px
|
26
|
+
|
27
|
+
.side-container
|
28
|
+
flex: 3
|
29
|
+
|
30
|
+
|
31
|
+
.no-padding
|
32
|
+
padding: 0 !important
|
33
|
+
|
34
|
+
.card-no-padding-title
|
35
|
+
padding: 18px 30px
|
36
|
+
|
@@ -0,0 +1,135 @@
|
|
1
|
+
/* http://meyerweb.com/eric/tools/css/reset/
|
2
|
+
v2.0 | 20110126
|
3
|
+
License: none (public domain)
|
4
|
+
*/
|
5
|
+
|
6
|
+
html,
|
7
|
+
body,
|
8
|
+
div,
|
9
|
+
span,
|
10
|
+
applet,
|
11
|
+
object,
|
12
|
+
iframe,
|
13
|
+
h1,
|
14
|
+
h2,
|
15
|
+
h3,
|
16
|
+
h4,
|
17
|
+
h5,
|
18
|
+
h6,
|
19
|
+
p,
|
20
|
+
blockquote,
|
21
|
+
pre,
|
22
|
+
a,
|
23
|
+
abbr,
|
24
|
+
acronym,
|
25
|
+
address,
|
26
|
+
big,
|
27
|
+
cite,
|
28
|
+
code,
|
29
|
+
del,
|
30
|
+
dfn,
|
31
|
+
em,
|
32
|
+
img,
|
33
|
+
ins,
|
34
|
+
kbd,
|
35
|
+
q,
|
36
|
+
s,
|
37
|
+
samp,
|
38
|
+
small,
|
39
|
+
strike,
|
40
|
+
strong,
|
41
|
+
sub,
|
42
|
+
sup,
|
43
|
+
tt,
|
44
|
+
var,
|
45
|
+
b,
|
46
|
+
u,
|
47
|
+
i,
|
48
|
+
center,
|
49
|
+
dl,
|
50
|
+
dt,
|
51
|
+
dd,
|
52
|
+
ol,
|
53
|
+
ul,
|
54
|
+
li,
|
55
|
+
fieldset,
|
56
|
+
form,
|
57
|
+
label,
|
58
|
+
legend,
|
59
|
+
table,
|
60
|
+
caption,
|
61
|
+
tbody,
|
62
|
+
tfoot,
|
63
|
+
thead,
|
64
|
+
tr,
|
65
|
+
th,
|
66
|
+
td,
|
67
|
+
article,
|
68
|
+
aside,
|
69
|
+
canvas,
|
70
|
+
details,
|
71
|
+
embed,
|
72
|
+
figure,
|
73
|
+
figcaption,
|
74
|
+
footer,
|
75
|
+
header,
|
76
|
+
hgroup,
|
77
|
+
menu,
|
78
|
+
nav,
|
79
|
+
output,
|
80
|
+
ruby,
|
81
|
+
section,
|
82
|
+
summary,
|
83
|
+
time,
|
84
|
+
mark,
|
85
|
+
audio,
|
86
|
+
video {
|
87
|
+
margin: 0;
|
88
|
+
padding: 0;
|
89
|
+
border: 0;
|
90
|
+
font-size: 100%;
|
91
|
+
font: inherit;
|
92
|
+
vertical-align: baseline;
|
93
|
+
}
|
94
|
+
|
95
|
+
/* HTML5 display-role reset for older browsers */
|
96
|
+
article,
|
97
|
+
aside,
|
98
|
+
details,
|
99
|
+
figcaption,
|
100
|
+
figure,
|
101
|
+
footer,
|
102
|
+
header,
|
103
|
+
hgroup,
|
104
|
+
menu,
|
105
|
+
nav,
|
106
|
+
section {
|
107
|
+
display: block;
|
108
|
+
}
|
109
|
+
|
110
|
+
body {
|
111
|
+
line-height: 1;
|
112
|
+
}
|
113
|
+
|
114
|
+
ol,
|
115
|
+
ul {
|
116
|
+
list-style: none;
|
117
|
+
}
|
118
|
+
|
119
|
+
blockquote,
|
120
|
+
q {
|
121
|
+
quotes: none;
|
122
|
+
}
|
123
|
+
|
124
|
+
blockquote:before,
|
125
|
+
blockquote:after,
|
126
|
+
q:before,
|
127
|
+
q:after {
|
128
|
+
content: '';
|
129
|
+
content: none;
|
130
|
+
}
|
131
|
+
|
132
|
+
table {
|
133
|
+
border-collapse: collapse;
|
134
|
+
border-spacing: 0;
|
135
|
+
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'kaminari'
|
2
|
+
module JobsDashboard
|
3
|
+
class ApplicationController < ActionController::Base
|
4
|
+
before_action :basic_auth
|
5
|
+
before_action :set_locale
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def basic_auth
|
10
|
+
if ENV["JOBS_DASHBOARD_AUTH_USERNAME"].blank? || ENV["JOBS_DASHBOARD_AUTH_PASSWORD"].blank?
|
11
|
+
raise 'Define a JOBS_DASHBOARD_AUTH_USERNAME and JOBS_DASHBOARD_AUTH_PASSWORD to access jobs dashboard'
|
12
|
+
end
|
13
|
+
self.class.http_basic_authenticate_with name: ENV["JOBS_DASHBOARD_AUTH_USERNAME"], password: ENV["JOBS_DASHBOARD_AUTH_PASSWORD"]
|
14
|
+
end
|
15
|
+
|
16
|
+
def set_locale
|
17
|
+
I18n.locale = :fr
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module JobsDashboard
|
2
|
+
class JobLogsController < ApplicationController
|
3
|
+
def index
|
4
|
+
@q = JobLog.ransack(params[:q])
|
5
|
+
|
6
|
+
@job_logs = @q.result(distinct: true).order(created_at: :desc)
|
7
|
+
if @job_logs.respond_to?(:page)
|
8
|
+
@job_logs = @job_logs.page(params[:page]).per(100)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def show
|
13
|
+
@job_log = JobLog.find_by(sidekiq_jid: params[:id])
|
14
|
+
@title = "#{@job_log.item_type}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module JobsDashboard
|
3
|
+
module ApplicationHelper
|
4
|
+
def display_time time
|
5
|
+
time.in_time_zone(DEFAULT_LOCAL_TIME_ZONE).strftime("%Y-%m-%d %I:%M:%S")
|
6
|
+
end
|
7
|
+
|
8
|
+
def status_label status
|
9
|
+
case status
|
10
|
+
when 'complete'
|
11
|
+
return 'success'
|
12
|
+
when 'failed', 'interrupted'
|
13
|
+
return 'error'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def item_types_collection
|
18
|
+
JobLog.pluck(:item_type).compact.uniq.sort
|
19
|
+
end
|
20
|
+
|
21
|
+
def statuses_collection
|
22
|
+
JobLog.statuses.keys.map do |key|
|
23
|
+
[I18n.t("jobs_dashboard.statuses.#{key}"), key]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def queues_collection
|
28
|
+
JobLog.pluck(:queue).compact.uniq.sort
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module JobsDashboard
|
2
|
+
DEFAULT_LOCAL_TIME_ZONE = 'Europe/Paris'
|
3
|
+
|
4
|
+
class JobLog < ApplicationRecord
|
5
|
+
enum status: {
|
6
|
+
queued: 'queued',
|
7
|
+
working: 'working',
|
8
|
+
retrying: 'retrying',
|
9
|
+
complete: 'complete',
|
10
|
+
failed: 'failed',
|
11
|
+
interrupted: 'interrupted'
|
12
|
+
}
|
13
|
+
|
14
|
+
serialize :args, Array
|
15
|
+
serialize :metadata, Hash
|
16
|
+
serialize :logs, Array
|
17
|
+
serialize :backtrace, Array
|
18
|
+
|
19
|
+
validates :sidekiq_jid, presence: true, uniqueness: true
|
20
|
+
|
21
|
+
def self.ransackable_attributes(auth_object = nil)
|
22
|
+
[
|
23
|
+
"sidekiq_jid",
|
24
|
+
"item_type",
|
25
|
+
"queue",
|
26
|
+
"status",
|
27
|
+
"created_at",
|
28
|
+
"finished_at",
|
29
|
+
]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
.row
|
2
|
+
.col-24
|
3
|
+
.card.shadowed.full-width
|
4
|
+
.card-content
|
5
|
+
.statistics
|
6
|
+
- [:working, :queued, :complete, :failed].each do |state|
|
7
|
+
.stat
|
8
|
+
strong
|
9
|
+
= @stats.find{|stat| stat[:status] === state.to_s }.try(:[], :count) || 0
|
10
|
+
= t state, scope: :'jobs_dashboard.statuses'
|
11
|
+
|
12
|
+
.row.mt-1
|
13
|
+
.col-12
|
14
|
+
.card.shadowed.full-width
|
15
|
+
.card-title
|
16
|
+
=> t :'jobs_dashboard.statuses.working'
|
17
|
+
- if @ongoing_jobs.count > 0
|
18
|
+
|(#{@ongoing_jobs.count})
|
19
|
+
.card-content
|
20
|
+
- if @ongoing_jobs.any?
|
21
|
+
ul.item-list
|
22
|
+
- @ongoing_jobs.limit(10).each do |job|
|
23
|
+
= render partial: 'job_row', locals: { job: job }
|
24
|
+
- else
|
25
|
+
small.text-muted
|
26
|
+
= t :'jobs_dashboard.sentences.no_ongoing_jobs'
|
27
|
+
.col-12
|
28
|
+
.card.shadowed.full-width
|
29
|
+
.card-title
|
30
|
+
=> t :'jobs_dashboard.statuses.queued'
|
31
|
+
- if @awaiting_jobs.count > 0
|
32
|
+
|(#{@awaiting_jobs.count})
|
33
|
+
.card-content
|
34
|
+
- if @awaiting_jobs.any?
|
35
|
+
ul.item-list
|
36
|
+
- @awaiting_jobs.limit(10).each do |job|
|
37
|
+
= render partial: 'job_row', locals: { job: job }
|
38
|
+
- else
|
39
|
+
small.text-muted
|
40
|
+
= t :'jobs_dashboard.sentences.no_awaiting_jobs'
|
File without changes
|
@@ -0,0 +1,84 @@
|
|
1
|
+
.card
|
2
|
+
.card-content
|
3
|
+
div
|
4
|
+
= search_form_for @q do |f|
|
5
|
+
.form-inline
|
6
|
+
.input
|
7
|
+
= f.label :sidekiq_jid_eq, t(:sidekiq_jid, scope: 'jobs_dashboard.table.headers')
|
8
|
+
= f.search_field :sidekiq_jid_eq
|
9
|
+
.input
|
10
|
+
= f.label :item_type_eq, t(:item_type, scope: 'jobs_dashboard.table.headers')
|
11
|
+
= f.select :item_type_eq,
|
12
|
+
options_for_select(item_types_collection,
|
13
|
+
params.dig(:q, :item_type_eq)),
|
14
|
+
{ include_blank: t('form.select.all') },
|
15
|
+
{ multiple: false }
|
16
|
+
.input
|
17
|
+
= f.label :status_eq, t(:status, scope: 'jobs_dashboard.table.headers')
|
18
|
+
= f.select :status_eq,
|
19
|
+
options_for_select(statuses_collection,
|
20
|
+
params.dig(:q, :status_eq)),
|
21
|
+
{ include_blank: t('form.select.all') },
|
22
|
+
{ multiple: false }
|
23
|
+
.input
|
24
|
+
= f.label :queue_eq, t(:queue, scope: 'jobs_dashboard.table.headers')
|
25
|
+
= f.select :queue_eq,
|
26
|
+
options_for_select(queues_collection,
|
27
|
+
params.dig(:q, :queue_eq)),
|
28
|
+
{ include_blank: t('form.select.all') },
|
29
|
+
{ multiple: false }
|
30
|
+
.input.group
|
31
|
+
= f.label :created_at_dategteq, t(:created_at, scope: 'jobs_dashboard.table.headers')
|
32
|
+
.d-flex.align-items-center
|
33
|
+
= f.date_field :created_at_dategteq
|
34
|
+
div style="margin-right: 5px; margin-left: 5px" = t('jobs_dashboard.words.to')
|
35
|
+
= f.date_field :created_at_datelteq
|
36
|
+
|
37
|
+
.input.group
|
38
|
+
= f.label :finished_at_dategteq, t(:finished_at, scope: 'jobs_dashboard.table.headers')
|
39
|
+
.d-flex.align-items-center
|
40
|
+
= f.date_field :finished_at_dategteq
|
41
|
+
div style="margin-right: 5px; margin-left: 5px" = t('jobs_dashboard.words.to')
|
42
|
+
= f.date_field :finished_at_datelteq
|
43
|
+
.form-inline.submit
|
44
|
+
= link_to t('jobs_dashboard.sentences.clear_filters'), job_logs_path
|
45
|
+
div style="margin-left: 30px"
|
46
|
+
= f.submit t('jobs_dashboard.words.submit'), class: 'button primary'
|
47
|
+
.row.mt-2
|
48
|
+
.col-24
|
49
|
+
table class="full-width"
|
50
|
+
thead
|
51
|
+
tr
|
52
|
+
th
|
53
|
+
= t(:sidekiq_jid, scope: 'jobs_dashboard.table.headers')
|
54
|
+
th
|
55
|
+
= t(:status, scope: 'jobs_dashboard.table.headers')
|
56
|
+
th
|
57
|
+
= t(:queue, scope: 'jobs_dashboard.table.headers')
|
58
|
+
th
|
59
|
+
= t(:item_type, scope: 'jobs_dashboard.table.headers')
|
60
|
+
- JobsDashboard.additional_params.each do |param|
|
61
|
+
th= param[:title]
|
62
|
+
th.text-right
|
63
|
+
= t(:created_at, scope: 'jobs_dashboard.table.headers')
|
64
|
+
th.text-right
|
65
|
+
= t(:finished_at, scope: 'jobs_dashboard.table.headers')
|
66
|
+
tbody
|
67
|
+
- @job_logs.each do |job_log|
|
68
|
+
tr
|
69
|
+
td= link_to job_log.sidekiq_jid, job_log_path(job_log.sidekiq_jid)
|
70
|
+
td
|
71
|
+
.status-label class=(status_label(job_log.status))
|
72
|
+
= t job_log.status, scope: 'jobs_dashboard.statuses'
|
73
|
+
td= job_log.queue
|
74
|
+
td= job_log.item_type
|
75
|
+
- JobsDashboard.additional_params.each do |param|
|
76
|
+
td
|
77
|
+
= job_log.try(:[], param[:attribute_name])
|
78
|
+
td.text-right
|
79
|
+
= display_time job_log.created_at
|
80
|
+
td.text-right
|
81
|
+
= job_log.finished_at.present? ? display_time(job_log.finished_at) : '-'
|
82
|
+
|
83
|
+
.pagination
|
84
|
+
= paginate @job_logs, theme: 'jobs_dashboard'
|
@@ -0,0 +1,89 @@
|
|
1
|
+
.page-container
|
2
|
+
.page-body
|
3
|
+
.main-container
|
4
|
+
- if @job_log.backtrace.present?
|
5
|
+
.card.shadowed
|
6
|
+
.card-title Error message
|
7
|
+
.card-content
|
8
|
+
code
|
9
|
+
= @job_log.error_message
|
10
|
+
|
11
|
+
.card.shadowed
|
12
|
+
.card-title Backtrace
|
13
|
+
.card-content
|
14
|
+
div.radius style="max-height: 400px; overflow-y: auto;"
|
15
|
+
.code
|
16
|
+
code
|
17
|
+
- @job_log.backtrace.each do |b|
|
18
|
+
div = b
|
19
|
+
|
20
|
+
- if @job_log.args.present?
|
21
|
+
.card.shadowed
|
22
|
+
.card-title Arguments
|
23
|
+
.card-content.no-padding
|
24
|
+
div.radius style="max-height: 400px; overflow-y: auto;"
|
25
|
+
.description
|
26
|
+
- @job_log.args.each do |arg|
|
27
|
+
- if arg.is_a? Hash
|
28
|
+
- arg.each do |k,v|
|
29
|
+
.item
|
30
|
+
.title
|
31
|
+
code = k
|
32
|
+
.value
|
33
|
+
code = v
|
34
|
+
- else
|
35
|
+
.item
|
36
|
+
.title
|
37
|
+
code = @job_log.args.index(arg)
|
38
|
+
.value
|
39
|
+
code = arg
|
40
|
+
|
41
|
+
- if @job_log.respond_to?(:metadata) && @job_log.metadata.present?
|
42
|
+
.card.shadowed.no-padding
|
43
|
+
.card-title Metadata
|
44
|
+
.card-content
|
45
|
+
div.radius style="max-height: 400px; overflow-y: auto;"
|
46
|
+
.description
|
47
|
+
- @job_log.metadata.each do |k, v|
|
48
|
+
.item
|
49
|
+
.title
|
50
|
+
code = k
|
51
|
+
.value
|
52
|
+
code = v
|
53
|
+
|
54
|
+
- if @job_log.respond_to?(:logs) && @job_log.logs.any?
|
55
|
+
.card.shadowed
|
56
|
+
.card-title Logs
|
57
|
+
.card-content.no-padding
|
58
|
+
div.radius style="max-height: 400px; overflow-y: auto;"
|
59
|
+
.description
|
60
|
+
- @job_log.logs.each do |log|
|
61
|
+
.item
|
62
|
+
.title = display_time(log[:created_at])
|
63
|
+
.value = log[:value]
|
64
|
+
|
65
|
+
.side-container
|
66
|
+
.card.shadowed
|
67
|
+
.card-title Infos
|
68
|
+
.card-content.no-padding
|
69
|
+
.description
|
70
|
+
.item
|
71
|
+
.title = t(:job_id, scope: 'jobs_dashboard.table.headers')
|
72
|
+
.value = @job_log.sidekiq_jid
|
73
|
+
.item
|
74
|
+
.title = t(:item_type, scope: 'jobs_dashboard.table.headers')
|
75
|
+
.value = @job_log.item_type
|
76
|
+
.item
|
77
|
+
.title = t(:created_at, scope: 'jobs_dashboard.table.headers')
|
78
|
+
.value = display_time(@job_log.created_at)
|
79
|
+
.item
|
80
|
+
.title = t(:finished_at, scope: 'jobs_dashboard.table.headers')
|
81
|
+
.value = @job_log.finished_at.present? ? display_time(@job_log.finished_at) : '-'
|
82
|
+
.item
|
83
|
+
.title = t(:status, scope: 'jobs_dashboard.table.headers')
|
84
|
+
.value
|
85
|
+
.status-label class=(status_label(@job_log.status))
|
86
|
+
= t @job_log.status, scope: 'jobs_dashboard.statuses'
|
87
|
+
.item
|
88
|
+
.title = t(:queue, scope: 'jobs_dashboard.table.headers')
|
89
|
+
.value = @job_log.queue
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<%# Link to the "First" page
|
2
|
+
- available local variables
|
3
|
+
url: url to the first page
|
4
|
+
current_page: a page object for the currently displayed page
|
5
|
+
total_pages: total number of pages
|
6
|
+
per_page: number of items to fetch per page
|
7
|
+
remote: data-remote
|
8
|
+
-%>
|
9
|
+
<span class="first">
|
10
|
+
<%= link_to_unless current_page.first?, t('views.pagination.first').html_safe, url, remote: remote %>
|
11
|
+
</span>
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<%# Non-link tag that stands for skipped pages...
|
2
|
+
- available local variables
|
3
|
+
current_page: a page object for the currently displayed page
|
4
|
+
total_pages: total number of pages
|
5
|
+
per_page: number of items to fetch per page
|
6
|
+
remote: data-remote
|
7
|
+
-%>
|
8
|
+
<span class="page gap"><%= t('views.pagination.truncate').html_safe %></span>
|