solid_queue_dashboard 0.0.1 → 0.1.0
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/.rubocop.yml +3 -0
- data/CHANGELOG.md +1 -1
- data/Procfile.dev +2 -0
- data/README.md +72 -21
- data/app/assets/javascripts/solid_queue_dashboard/alpine.js +5 -0
- data/app/assets/javascripts/solid_queue_dashboard/application.js +46 -0
- data/app/assets/stylesheets/solid_queue_dashboard/application.css +2062 -0
- data/app/assets/stylesheets/solid_queue_dashboard/tailwind.css +554 -0
- data/app/controllers/solid_queue_dashboard/appearance_controller.rb +8 -0
- data/app/controllers/solid_queue_dashboard/application_controller.rb +7 -0
- data/app/controllers/solid_queue_dashboard/dashboard_controller.rb +8 -0
- data/app/controllers/solid_queue_dashboard/jobs_controller.rb +42 -0
- data/app/controllers/solid_queue_dashboard/processes_controller.rb +28 -0
- data/app/controllers/solid_queue_dashboard/recurring_tasks_controller.rb +30 -0
- data/app/helpers/solid_queue_dashboard/appearance_helper.rb +7 -0
- data/app/helpers/solid_queue_dashboard/application_helper.rb +7 -0
- data/app/helpers/solid_queue_dashboard/icons_helper.rb +231 -0
- data/app/helpers/solid_queue_dashboard/jobs_helper.rb +51 -0
- data/app/helpers/solid_queue_dashboard/pagination_helper.rb +38 -0
- data/app/helpers/solid_queue_dashboard/processes_helper.rb +35 -0
- data/app/helpers/solid_queue_dashboard/recurring_tasks_helper.rb +33 -0
- data/app/views/layouts/solid_queue_dashboard/application.html.erb +21 -0
- data/app/views/solid_queue_dashboard/application/_flash_messages.html.erb +38 -0
- data/app/views/solid_queue_dashboard/application/_footer.html.erb +11 -0
- data/app/views/solid_queue_dashboard/application/_navbar.html.erb +50 -0
- data/app/views/solid_queue_dashboard/application/_pagination.html.erb +19 -0
- data/app/views/solid_queue_dashboard/dashboard/index.html.erb +41 -0
- data/app/views/solid_queue_dashboard/jobs/_filters.html.erb +87 -0
- data/app/views/solid_queue_dashboard/jobs/_table.html.erb +19 -0
- data/app/views/solid_queue_dashboard/jobs/_table_row.html.erb +82 -0
- data/app/views/solid_queue_dashboard/jobs/index.html.erb +57 -0
- data/app/views/solid_queue_dashboard/jobs/show.html.erb +192 -0
- data/app/views/solid_queue_dashboard/processes/_filters.html.erb +64 -0
- data/app/views/solid_queue_dashboard/processes/_table.html.erb +18 -0
- data/app/views/solid_queue_dashboard/processes/_table_row.html.erb +32 -0
- data/app/views/solid_queue_dashboard/processes/index.html.erb +27 -0
- data/app/views/solid_queue_dashboard/processes/show.html.erb +79 -0
- data/app/views/solid_queue_dashboard/recurring_tasks/_filters.html.erb +26 -0
- data/app/views/solid_queue_dashboard/recurring_tasks/_table.html.erb +19 -0
- data/app/views/solid_queue_dashboard/recurring_tasks/_table_row.html.erb +31 -0
- data/app/views/solid_queue_dashboard/recurring_tasks/index.html.erb +21 -0
- data/app/views/solid_queue_dashboard/recurring_tasks/show.html.erb +129 -0
- data/bun.lockb +0 -0
- data/config/routes.rb +18 -0
- data/lib/solid_queue_dashboard/configuration.rb +17 -0
- data/lib/solid_queue_dashboard/decorators/job_decorator.rb +63 -0
- data/lib/solid_queue_dashboard/decorators/jobs_decorator.rb +74 -0
- data/lib/solid_queue_dashboard/decorators/process_decorator.rb +13 -0
- data/lib/solid_queue_dashboard/decorators/processes_decorator.rb +15 -0
- data/lib/solid_queue_dashboard/decorators/recurring_task_decorator.rb +22 -0
- data/lib/solid_queue_dashboard/decorators/recurring_tasks_decorator.rb +26 -0
- data/lib/solid_queue_dashboard/engine.rb +13 -0
- data/lib/solid_queue_dashboard/job.rb +26 -0
- data/lib/solid_queue_dashboard/process.rb +24 -0
- data/lib/solid_queue_dashboard/recurring_task.rb +14 -0
- data/lib/solid_queue_dashboard/version.rb +1 -1
- data/lib/solid_queue_dashboard.rb +39 -1
- data/package.json +13 -0
- data/tailwind.config.js +83 -0
- metadata +72 -3
@@ -0,0 +1,19 @@
|
|
1
|
+
<% highlight_ids = local_assigns[:highlight_ids] || [] %>
|
2
|
+
|
3
|
+
<div class="table-wrapper">
|
4
|
+
<table class="table">
|
5
|
+
<thead class="table-header">
|
6
|
+
<tr class="table-row">
|
7
|
+
<th class="table-head pl-6">ID</th>
|
8
|
+
<th class="table-head">Status</th>
|
9
|
+
<th class="table-head">Class</th>
|
10
|
+
<th class="table-head">Queue</th>
|
11
|
+
<th class="table-head">Scheduled At</th>
|
12
|
+
<th class="table-head"></th>
|
13
|
+
</tr>
|
14
|
+
</thead>
|
15
|
+
<tbody class="table-body">
|
16
|
+
<%= render partial: 'solid_queue_dashboard/jobs/table_row', collection: jobs, as: :job, locals: { highlight_ids: highlight_ids } %>
|
17
|
+
</tbody>
|
18
|
+
</table>
|
19
|
+
</div>
|
@@ -0,0 +1,82 @@
|
|
1
|
+
<% highlight = local_assigns[:highlight] || local_assigns[:highlight_ids]&.include?(job.id) %>
|
2
|
+
|
3
|
+
<tr class="table-row <%= highlight ? "bg-primary/7.5 hover:bg-primary/10" : "" %>" data-href="<%= job_path(job) %>">
|
4
|
+
<td class="table-cell pl-6 font-medium text-zinc-900 dark:text-zinc-100 whitespace-nowrap">
|
5
|
+
<span class="mr-1 <%= job_status_circle_class(job.status) %>"></span>
|
6
|
+
<%= job.id %>
|
7
|
+
|
8
|
+
<% if job.arguments["executions"] > 0 %>
|
9
|
+
<span class="text-xs text-muted-foreground">
|
10
|
+
(x<%= job.arguments["executions"] + 1 %>)
|
11
|
+
</span>
|
12
|
+
<% end %>
|
13
|
+
</td>
|
14
|
+
|
15
|
+
<td class="table-cell">
|
16
|
+
<%= job_status_badge(job.status) %>
|
17
|
+
</td>
|
18
|
+
|
19
|
+
<td class="table-cell">
|
20
|
+
<% if job.class_name == SolidQueueDashboard::Job::COMMAND_CLASS_NAME %>
|
21
|
+
<span class="font-medium">
|
22
|
+
<%= truncate(job.arguments["arguments"][0], length: 50) %>
|
23
|
+
</span>
|
24
|
+
<br>
|
25
|
+
<span class="text-xs text-muted-foreground">
|
26
|
+
Recurring Command
|
27
|
+
</span>
|
28
|
+
<% else %>
|
29
|
+
<p class="font-medium"><%= job.class_name %></p>
|
30
|
+
|
31
|
+
<% if job.arguments["arguments"].present? %>
|
32
|
+
<p class="mt-0.5">
|
33
|
+
<% job.arguments["arguments"].each do |argument| %>
|
34
|
+
<span class="badge badge-zinc"><%= truncate(JSON.pretty_generate(argument), length: 80) %></span>
|
35
|
+
<% end %>
|
36
|
+
</p>
|
37
|
+
<% end %>
|
38
|
+
<% end %>
|
39
|
+
</td>
|
40
|
+
|
41
|
+
<td class="table-cell">
|
42
|
+
<%= job.queue_name.titleize %>
|
43
|
+
</td>
|
44
|
+
|
45
|
+
<td class="table-cell">
|
46
|
+
<%= tag.span job.scheduled_at, data: { date: true }, class: "font-medium" %>
|
47
|
+
<br />
|
48
|
+
<span class="text-xs text-muted-foreground">
|
49
|
+
<%= time_ago_in_words(job.scheduled_at, include_seconds: true) %> ago
|
50
|
+
</span>
|
51
|
+
</td>
|
52
|
+
|
53
|
+
<td class="table-cell">
|
54
|
+
<% if job.success? || job.retried? %>
|
55
|
+
<p class="text-sm text-muted-foreground">
|
56
|
+
<%= job.retried? ? "Failed" : "Finished" %> at <strong class="font-medium text-foreground" data-date="<%= job.finished_at.to_fs(:database) %>"><%= job.finished_at %></strong><br />
|
57
|
+
<span class="text-xs"><%= time_ago_in_words(job.scheduled_at, include_seconds: true) %> ago</span>
|
58
|
+
</p>
|
59
|
+
<% elsif job.failed? %>
|
60
|
+
<div class="flex items-center justify-between">
|
61
|
+
<p class="text-sm text-balance font-medium">
|
62
|
+
<%= icon_triangle_alert class: "inline-block size-4 text-red-600 dark:text-red-500 -translate-y-px mr-0.5" %>
|
63
|
+
<%= job.error_message %>
|
64
|
+
</p>
|
65
|
+
|
66
|
+
<%= form_with url: retry_job_path(job), method: :post do %>
|
67
|
+
<button
|
68
|
+
type="submit"
|
69
|
+
class="btn btn-icon btn-outline"
|
70
|
+
title="Retry"
|
71
|
+
>
|
72
|
+
<%= icon_refresh_cw class: "size-4 text-amber-500" %>
|
73
|
+
</button>
|
74
|
+
<% end %>
|
75
|
+
</div>
|
76
|
+
<% elsif job.pending? %>
|
77
|
+
<p class="text-sm text-muted-foreground">
|
78
|
+
Pending for <strong class="font-medium text-foreground"><%= time_ago_in_words(job.scheduled_at, include_seconds: true) %></strong>
|
79
|
+
</p>
|
80
|
+
<% end %>
|
81
|
+
</td>
|
82
|
+
</tr>
|
@@ -0,0 +1,57 @@
|
|
1
|
+
<div class="card">
|
2
|
+
<div class="card-header border-b">
|
3
|
+
<h2 class="card-title">Jobs</h2>
|
4
|
+
|
5
|
+
<div class="!mt-4">
|
6
|
+
<%= render "filters" %>
|
7
|
+
</div>
|
8
|
+
</div>
|
9
|
+
|
10
|
+
<div class="card-content !p-0">
|
11
|
+
<% if @jobs.any? %>
|
12
|
+
<%= render "solid_queue_dashboard/jobs/table", jobs: @jobs %>
|
13
|
+
<% else %>
|
14
|
+
<div class="px-6 py-12">
|
15
|
+
<p class="text-muted-foreground text-center">
|
16
|
+
No jobs found
|
17
|
+
</p>
|
18
|
+
|
19
|
+
<% if any_jobs_filters? %>
|
20
|
+
<div class="flex justify-center mt-2">
|
21
|
+
<%= link_to "Clear Filters", jobs_path, class: "btn btn-outline" %>
|
22
|
+
</div>
|
23
|
+
<% end %>
|
24
|
+
</div>
|
25
|
+
<% end %>
|
26
|
+
</div>
|
27
|
+
|
28
|
+
<% if @pagination.total_pages > 1 %>
|
29
|
+
<div class="card-footer pt-6 border-t flex justify-between">
|
30
|
+
<%= render partial: 'solid_queue_dashboard/application/pagination', locals: {
|
31
|
+
current_page: @pagination.current_page,
|
32
|
+
total_pages: @pagination.total_pages,
|
33
|
+
per_page: @pagination.per_page
|
34
|
+
} %>
|
35
|
+
|
36
|
+
<div class="flex items-center gap-2">
|
37
|
+
<span class="text-muted-foreground text-sm whitespace-nowrap">
|
38
|
+
Show
|
39
|
+
</span>
|
40
|
+
|
41
|
+
<%= form_with url: jobs_path, method: :get do |f| %>
|
42
|
+
<%= f.hidden_field :page, value: 1 %>
|
43
|
+
<%= f.select :per_page,
|
44
|
+
[10, 25, 50, 100],
|
45
|
+
{ selected: @pagination.per_page },
|
46
|
+
class: "select w-24",
|
47
|
+
data: { auto_submit: true }
|
48
|
+
%>
|
49
|
+
<% end %>
|
50
|
+
|
51
|
+
<span class="text-muted-foreground text-sm whitespace-nowrap">
|
52
|
+
per page
|
53
|
+
</span>
|
54
|
+
</div>
|
55
|
+
</div>
|
56
|
+
<% end %>
|
57
|
+
</div>
|
@@ -0,0 +1,192 @@
|
|
1
|
+
<div class="flex items-center justify-between gap-4">
|
2
|
+
<h1 class="text-4xl flex items-center gap-2">
|
3
|
+
<%= job_status_circle(@job.status, class: "size-4") %>
|
4
|
+
|
5
|
+
<span class="tracking-tighter font-bold">
|
6
|
+
Job #<%= @job.id %>
|
7
|
+
</span>
|
8
|
+
|
9
|
+
<% if @job.arguments["executions"] > 0 %>
|
10
|
+
<span class="ml-2 badge badge-zinc">
|
11
|
+
<%= (@job.arguments["executions"] + 1).ordinalize %> attempt
|
12
|
+
</span>
|
13
|
+
<% end %>
|
14
|
+
</h1>
|
15
|
+
|
16
|
+
<div class="flex gap-2">
|
17
|
+
<% if @job.failed? %>
|
18
|
+
<%= form_with url: retry_job_path(@job) do %>
|
19
|
+
<button class="btn btn-outline btn-md">
|
20
|
+
<%= icon_refresh_cw class: "size-4 text-amber-600" %>
|
21
|
+
<span>Retry Job</span>
|
22
|
+
</button>
|
23
|
+
<% end %>
|
24
|
+
<% end %>
|
25
|
+
</div>
|
26
|
+
</div>
|
27
|
+
|
28
|
+
<div class="card mt-6">
|
29
|
+
<div class="card-content pt-6 grid grid-cols-1 sm:grid-cols-2 sm:gap-16 md:gap-20">
|
30
|
+
<div class="space-y-4">
|
31
|
+
<div class="info-line">
|
32
|
+
<span class="info-line-label">Status</span>
|
33
|
+
<span class="info-line-separator"></span>
|
34
|
+
<span class="info-line-value"><%= job_status_badge(@job.status) %></span>
|
35
|
+
</div>
|
36
|
+
|
37
|
+
<div class="info-line">
|
38
|
+
<span class="info-line-label">Queue</span>
|
39
|
+
<span class="info-line-separator"></span>
|
40
|
+
<span class="info-line-value">
|
41
|
+
<%= link_to @job.queue_name.titleize, jobs_path(queue_name: @job.queue_name), class: "link" %>
|
42
|
+
</span>
|
43
|
+
</div>
|
44
|
+
|
45
|
+
<div class="info-line">
|
46
|
+
<span class="info-line-label">Class</span>
|
47
|
+
<span class="info-line-separator"></span>
|
48
|
+
<span class="info-line-value">
|
49
|
+
<%= link_to @job.class_name, jobs_path(class_name: @job.class_name), class: "link" %>
|
50
|
+
</span>
|
51
|
+
</div>
|
52
|
+
|
53
|
+
<div class="info-line">
|
54
|
+
<span class="info-line-label">Active Job ID</span>
|
55
|
+
<span class="info-line-separator"></span>
|
56
|
+
<span class="info-line-value"><%= @job.active_job_id %></span>
|
57
|
+
</div>
|
58
|
+
</div>
|
59
|
+
|
60
|
+
<div class="space-y-4">
|
61
|
+
<div class="info-line">
|
62
|
+
<span class="info-line-label">Created At</span>
|
63
|
+
<span class="info-line-separator"></span>
|
64
|
+
<span class="info-line-value" data-date><%= @job.created_at %></span>
|
65
|
+
</div>
|
66
|
+
|
67
|
+
<div class="info-line">
|
68
|
+
<span class="info-line-label">Scheduled At</span>
|
69
|
+
<span class="info-line-separator"></span>
|
70
|
+
<span class="info-line-value" data-date><%= @job.scheduled_at %></span>
|
71
|
+
</div>
|
72
|
+
|
73
|
+
<% if @job.success? || @job.retried? %>
|
74
|
+
<div class="info-line">
|
75
|
+
<span class="info-line-label">Finished At</span>
|
76
|
+
<span class="info-line-separator"></span>
|
77
|
+
<span class="text-sm text-muted-foreground font-normal">
|
78
|
+
<%= time_ago_in_words(@job.finished_at, include_seconds: true) %> ago
|
79
|
+
</span>
|
80
|
+
<span class="info-line-value" data-date><%= @job.finished_at.to_fs(:database) %></span>
|
81
|
+
</div>
|
82
|
+
|
83
|
+
<div class="info-line">
|
84
|
+
<span class="info-line-label">Time Taken</span>
|
85
|
+
<span class="info-line-separator"></span>
|
86
|
+
<span class="info-line-value"><%= distance_of_time_in_words(@job.scheduled_at, @job.finished_at, include_seconds: true) %></span>
|
87
|
+
</div>
|
88
|
+
<% end %>
|
89
|
+
|
90
|
+
<% if @job.failed? %>
|
91
|
+
<div class="info-line">
|
92
|
+
<span class="info-line-label">Failed At</span>
|
93
|
+
<span class="info-line-separator"></span>
|
94
|
+
<span class="info-line-value" data-date><%= @job.failed_execution.created_at.to_fs(:database) %></span>
|
95
|
+
</div>
|
96
|
+
|
97
|
+
<div class="info-line">
|
98
|
+
<span class="info-line-label">Time Taken</span>
|
99
|
+
<span class="info-line-separator"></span>
|
100
|
+
<span class="info-line-value"><%= distance_of_time_in_words(@job.scheduled_at, @job.failed_execution.created_at, include_seconds: true) %></span>
|
101
|
+
</div>
|
102
|
+
<% end %>
|
103
|
+
</div>
|
104
|
+
</div>
|
105
|
+
</div>
|
106
|
+
|
107
|
+
<% if @job.arguments["arguments"].present? %>
|
108
|
+
<div x-data="{ showAll: false }" class="card mt-8 overflow-hidden">
|
109
|
+
<div class="card-header border-b border-t-4 border-t-blue-500 dark:border-t-blue-600 flex flex-row justify-between gap-4">
|
110
|
+
<div class="space-y-1.5">
|
111
|
+
<% if @job.class_name === SolidQueueDashboard::Job::COMMAND_CLASS_NAME %>
|
112
|
+
<h3 class="card-title">Command</h3>
|
113
|
+
<p class="card-description">The command that was executed</p>
|
114
|
+
<% else %>
|
115
|
+
<h3 class="card-title">Arguments</h3>
|
116
|
+
<p class="card-description">The arguments that were passed to this job</p>
|
117
|
+
<% end %>
|
118
|
+
</div>
|
119
|
+
|
120
|
+
<button x-on:click="showAll = !showAll" class="btn btn-sm btn-outline">
|
121
|
+
<span x-show="!showAll">Show All Metadata</span>
|
122
|
+
<span x-show="showAll">Hide All Metadata</span>
|
123
|
+
</button>
|
124
|
+
</div>
|
125
|
+
<div class="card-content pt-6">
|
126
|
+
<% if @job.class_name === SolidQueueDashboard::Job::COMMAND_CLASS_NAME %>
|
127
|
+
<pre x-show="!showAll"><%= @job.arguments["arguments"][0] %></pre>
|
128
|
+
<% else %>
|
129
|
+
<% @job.arguments["arguments"].each do |argument| %>
|
130
|
+
<span class="badge badge-zinc text-sm"><%= JSON.pretty_generate(argument) %></span>
|
131
|
+
<% end %>
|
132
|
+
<% end %>
|
133
|
+
|
134
|
+
<pre x-show="showAll"><%= JSON.pretty_generate(@job.arguments) %></pre>
|
135
|
+
</div>
|
136
|
+
</div>
|
137
|
+
<% end %>
|
138
|
+
|
139
|
+
<% if @job.failed? %>
|
140
|
+
<div class="card mt-8 overflow-hidden">
|
141
|
+
<div class="card-content pt-5 border-t-4 border-t-red-500">
|
142
|
+
<h4 class="text-lg font-bold">
|
143
|
+
<%= @job.error_message %>
|
144
|
+
</h4>
|
145
|
+
|
146
|
+
<% backtrace = Rails.backtrace_cleaner.clean(@job.failed_execution.error["backtrace"]) %>
|
147
|
+
<% if backtrace.any? %>
|
148
|
+
<ul class="list-decimal marker:text-sm marker:text-muted-foreground mt-1 ml-8 space-y-1">
|
149
|
+
<% backtrace.each do |line| %>
|
150
|
+
<li class="font-mono"><%= line %></li>
|
151
|
+
<% end %>
|
152
|
+
</ul>
|
153
|
+
<% end %>
|
154
|
+
</div>
|
155
|
+
</div>
|
156
|
+
<% end %>
|
157
|
+
|
158
|
+
<% if @job.execution_history.size > 1 %>
|
159
|
+
<div class="card mt-8 overflow-hidden">
|
160
|
+
<div class="card-header border-b border-t-4 border-t-zinc-500 dark:border-t-zinc-600">
|
161
|
+
<h3 class="card-title">Execution History</h3>
|
162
|
+
<p class="card-description">The history of executions of this exact job</p>
|
163
|
+
</div>
|
164
|
+
|
165
|
+
<div class="card-content !p-0">
|
166
|
+
<%= render "table", jobs: SolidQueueDashboard.decorate(@job.execution_history.order(id: :desc)), highlight_ids: [@job.id] %>
|
167
|
+
</div>
|
168
|
+
</div>
|
169
|
+
<% end %>
|
170
|
+
|
171
|
+
<% if @job_history.size > 1 %>
|
172
|
+
<div class="card mt-8 overflow-hidden">
|
173
|
+
<div class="card-header border-b border-t-4 border-t-zinc-500 dark:border-t-zinc-600 flex !flex-row justify-between items-center">
|
174
|
+
<div class="space-y-1.5">
|
175
|
+
<h3 class="card-title">Similar Jobs</h3>
|
176
|
+
<p class="card-description">The history of executions of the same job class</p>
|
177
|
+
</div>
|
178
|
+
|
179
|
+
<%= link_to "View All", jobs_path(class_name: @job.class_name), class: "btn btn-outline btn-sm" %>
|
180
|
+
</div>
|
181
|
+
|
182
|
+
<div class="card-content !p-0">
|
183
|
+
<%= render "table", jobs: @job_history %>
|
184
|
+
</div>
|
185
|
+
|
186
|
+
<div class="card-footer border-t pt-6">
|
187
|
+
<p class="text-sm text-muted-foreground">
|
188
|
+
Showing the last 10 executions of this job
|
189
|
+
</p>
|
190
|
+
</div>
|
191
|
+
</div>
|
192
|
+
<% end %>
|
@@ -0,0 +1,64 @@
|
|
1
|
+
<%= form_with url: processes_path, method: :get, class: "space-y-3" do |form| %>
|
2
|
+
<% if @process_kinds.any? %>
|
3
|
+
<div>
|
4
|
+
<label class="label">Kind</label>
|
5
|
+
<div class="flex flex-wrap gap-1 mt-1">
|
6
|
+
<% @process_kinds.each do |kind| %>
|
7
|
+
<%= button_tag type: :submit,
|
8
|
+
name: :kind,
|
9
|
+
value: kind,
|
10
|
+
class: "px-2 badge #{params[:kind] == kind.to_s ? 'badge-primary' : 'badge-outline'}" do
|
11
|
+
%>
|
12
|
+
<%= process_kind_circle(kind) %>
|
13
|
+
<%= kind.to_s.titleize %>
|
14
|
+
<span class="opacity-50 -ml-0.5">
|
15
|
+
<%= SolidQueue::Process.where(kind:).count %>
|
16
|
+
</span>
|
17
|
+
<% end %>
|
18
|
+
<% end %>
|
19
|
+
|
20
|
+
<% if params[:kind].present? %>
|
21
|
+
<%= button_tag type: :submit, name: :kind, value: nil, class: "badge badge-destructive gap-1" do %>
|
22
|
+
<%= icon_x class: "size-3.5 text-red-500" %>
|
23
|
+
Clear
|
24
|
+
<% end %>
|
25
|
+
<% end %>
|
26
|
+
</div>
|
27
|
+
</div>
|
28
|
+
<% end %>
|
29
|
+
|
30
|
+
<% if @process_hostnames.any? %>
|
31
|
+
<div>
|
32
|
+
<label class="label">Hostname</label>
|
33
|
+
<div class="flex flex-wrap gap-1 mt-1">
|
34
|
+
<% @process_hostnames.each do |hostname| %>
|
35
|
+
<%= button_tag type: :submit,
|
36
|
+
name: :hostname,
|
37
|
+
value: hostname,
|
38
|
+
class: "px-2 badge #{params[:hostname] == hostname ? 'badge-primary' : 'badge-outline'}" do
|
39
|
+
%>
|
40
|
+
<%= hostname %>
|
41
|
+
<span class="opacity-50 -ml-0.5">
|
42
|
+
<%= SolidQueue::Process.where(hostname:).count %>
|
43
|
+
</span>
|
44
|
+
<% end %>
|
45
|
+
<% end %>
|
46
|
+
|
47
|
+
<% if params[:hostname].present? %>
|
48
|
+
<%= button_tag type: :submit, name: :hostname, value: nil, class: "badge badge-destructive gap-1" do %>
|
49
|
+
<%= icon_x class: "size-3.5 text-red-500" %>
|
50
|
+
Clear
|
51
|
+
<% end %>
|
52
|
+
<% end %>
|
53
|
+
</div>
|
54
|
+
</div>
|
55
|
+
<% end %>
|
56
|
+
|
57
|
+
<% if any_processes_filters? %>
|
58
|
+
<hr>
|
59
|
+
<%= link_to processes_path, class: "btn btn-outline btn-xs" do %>
|
60
|
+
<%= icon_x class: "size-4 text-red-500" %>
|
61
|
+
Clear All Filters
|
62
|
+
<% end %>
|
63
|
+
<% end %>
|
64
|
+
<% end %>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<div class="table-wrapper">
|
2
|
+
<table class="table">
|
3
|
+
<thead class="table-header">
|
4
|
+
<tr class="table-row">
|
5
|
+
<th class="table-head pl-6">ID</th>
|
6
|
+
<th class="table-head">Kind</th>
|
7
|
+
<th class="table-head">Hostname</th>
|
8
|
+
<th class="table-head">PID</th>
|
9
|
+
<th class="table-head">Launched At</th>
|
10
|
+
<th class="table-head">Last Heartbeat</th>
|
11
|
+
<th class="table-head"></th>
|
12
|
+
</tr>
|
13
|
+
</thead>
|
14
|
+
<tbody class="table-body">
|
15
|
+
<%= render partial: 'solid_queue_dashboard/processes/table_row', collection: processes, as: :process %>
|
16
|
+
</tbody>
|
17
|
+
</table>
|
18
|
+
</div>
|
@@ -0,0 +1,32 @@
|
|
1
|
+
<tr class="table-row" data-href="<%= process_path(process) %>">
|
2
|
+
<td class="table-cell pl-6 font-medium text-zinc-900 dark:text-zinc-100">
|
3
|
+
<%= process.id %>
|
4
|
+
</td>
|
5
|
+
|
6
|
+
<td class="table-cell">
|
7
|
+
<%= process_kind_badge(process.kind) %>
|
8
|
+
</td>
|
9
|
+
|
10
|
+
<td class="table-cell"><%= process.hostname %></td>
|
11
|
+
<td class="table-cell"><%= process.pid %></td>
|
12
|
+
|
13
|
+
<td class="table-cell">
|
14
|
+
<span data-date="true"><%= process.created_at.to_fs(:database) %></span><br />
|
15
|
+
<span class="text-xs text-muted-foreground">
|
16
|
+
<%= time_ago_in_words(process.created_at, include_seconds: true) %> ago
|
17
|
+
</span>
|
18
|
+
</td>
|
19
|
+
|
20
|
+
<td class="table-cell" title="<%= process.last_heartbeat_at %>">
|
21
|
+
<span data-date="true"><%= process.last_heartbeat_at.to_fs(:database) %></span><br />
|
22
|
+
<span class="text-xs text-muted-foreground">
|
23
|
+
<%= time_ago_in_words(process.last_heartbeat_at, include_seconds: true) %> ago
|
24
|
+
</span>
|
25
|
+
</td>
|
26
|
+
|
27
|
+
<td class="table-cell">
|
28
|
+
<% if process.dead? %>
|
29
|
+
<span class="badge badge-red">Dead?</span>
|
30
|
+
<% end %>
|
31
|
+
</td>
|
32
|
+
</tr>
|
@@ -0,0 +1,27 @@
|
|
1
|
+
<div class="card">
|
2
|
+
<div class="card-header border-b">
|
3
|
+
<h2 class="card-title">Processes</h2>
|
4
|
+
|
5
|
+
<div class="!mt-4">
|
6
|
+
<%= render "filters" %>
|
7
|
+
</div>
|
8
|
+
</div>
|
9
|
+
|
10
|
+
<div class="card-content !p-0">
|
11
|
+
<% if @processes.any? %>
|
12
|
+
<%= render "solid_queue_dashboard/processes/table", processes: @processes %>
|
13
|
+
<% else %>
|
14
|
+
<div class="px-6 py-12">
|
15
|
+
<p class="text-muted-foreground text-center">
|
16
|
+
No processes found
|
17
|
+
</p>
|
18
|
+
|
19
|
+
<% if any_processes_filters? %>
|
20
|
+
<div class="flex justify-center mt-2">
|
21
|
+
<%= link_to "Clear Filters", processes_path, class: "btn btn-outline btn-md" %>
|
22
|
+
</div>
|
23
|
+
<% end %>
|
24
|
+
</div>
|
25
|
+
<% end %>
|
26
|
+
</div>
|
27
|
+
</div>
|
@@ -0,0 +1,79 @@
|
|
1
|
+
<h1 class="text-4xl flex items-center gap-2">
|
2
|
+
<%= process_kind_circle(@process.kind, class: "!size-4") %>
|
3
|
+
|
4
|
+
<span class="tracking-tighter font-bold">
|
5
|
+
Process #<%= @process.id %>
|
6
|
+
</span>
|
7
|
+
|
8
|
+
<% if @process.dead? %>
|
9
|
+
<span class="badge badge-red">Dead?</span>
|
10
|
+
<% end %>
|
11
|
+
</h1>
|
12
|
+
|
13
|
+
<div class="card mt-6">
|
14
|
+
<div class="card-content pt-6 grid grid-cols-1 sm:grid-cols-2 sm:gap-16">
|
15
|
+
<div class="space-y-4">
|
16
|
+
<div class="info-line">
|
17
|
+
<span class="info-line-label">Kind</span>
|
18
|
+
<span class="info-line-separator"></span>
|
19
|
+
<span class="info-line-value"><%= process_kind_badge(@process.kind) %></span>
|
20
|
+
</div>
|
21
|
+
|
22
|
+
<div class="info-line">
|
23
|
+
<span class="info-line-label">Hostname</span>
|
24
|
+
<span class="info-line-separator"></span>
|
25
|
+
<span class="info-line-value"><%= @process.hostname %></span>
|
26
|
+
</div>
|
27
|
+
|
28
|
+
<div class="info-line">
|
29
|
+
<span class="info-line-label">PID</span>
|
30
|
+
<span class="info-line-separator"></span>
|
31
|
+
<span class="info-line-value"><%= @process.pid %></span>
|
32
|
+
</div>
|
33
|
+
|
34
|
+
<div class="info-line">
|
35
|
+
<span class="info-line-label">Name</span>
|
36
|
+
<span class="info-line-separator"></span>
|
37
|
+
<span class="info-line-value"><%= @process.name %></span>
|
38
|
+
</div>
|
39
|
+
</div>
|
40
|
+
|
41
|
+
<div class="space-y-4">
|
42
|
+
<div class="info-line">
|
43
|
+
<span class="info-line-label">Last Heartbeat</span>
|
44
|
+
<span class="info-line-separator"></span>
|
45
|
+
<span class="text-sm text-muted-foreground font-normal">
|
46
|
+
<%= time_ago_in_words(@process.last_heartbeat_at, include_seconds: true) %> ago
|
47
|
+
</span>
|
48
|
+
<span class="info-line-value" data-date><%= @process.last_heartbeat_at.to_fs(:database) %></span>
|
49
|
+
</div>
|
50
|
+
|
51
|
+
<div class="info-line">
|
52
|
+
<span class="info-line-label">Created At</span>
|
53
|
+
<span class="info-line-separator"></span>
|
54
|
+
<span class="text-sm text-muted-foreground font-normal">
|
55
|
+
<%= time_ago_in_words(@process.created_at, include_seconds: true) %> ago
|
56
|
+
</span>
|
57
|
+
<span class="info-line-value" data-date><%= @process.created_at.to_fs(:database) %></span>
|
58
|
+
</div>
|
59
|
+
|
60
|
+
<div class="info-line">
|
61
|
+
<span class="info-line-label">Time Running</span>
|
62
|
+
<span class="info-line-separator"></span>
|
63
|
+
<span class="info-line-value"><%= distance_of_time_in_words(@process.created_at, Time.current, include_seconds: true) %></span>
|
64
|
+
</div>
|
65
|
+
</div>
|
66
|
+
</div>
|
67
|
+
</div>
|
68
|
+
|
69
|
+
<% if @process.metadata.present? %>
|
70
|
+
<div class="card mt-8 overflow-hidden">
|
71
|
+
<div class="card-header border-b border-t-4 border-t-blue-500 dark:border-t-blue-600">
|
72
|
+
<h2 class="card-title">Metadata</h2>
|
73
|
+
<p class="card-description">Additional information about this process</p>
|
74
|
+
</div>
|
75
|
+
<div class="card-content pt-6">
|
76
|
+
<pre><%= JSON.pretty_generate(@process.metadata) %></pre>
|
77
|
+
</div>
|
78
|
+
</div>
|
79
|
+
<% end %>
|
@@ -0,0 +1,26 @@
|
|
1
|
+
<%= form_with url: recurring_tasks_path, method: :get, class: "space-y-3" do |form| %>
|
2
|
+
<div>
|
3
|
+
<div class="flex flex-wrap gap-1 mt-1">
|
4
|
+
<% SolidQueueDashboard::RecurringTask::TYPES.each do |type| %>
|
5
|
+
<%= button_tag type: :submit,
|
6
|
+
name: :type,
|
7
|
+
value: type,
|
8
|
+
class: "px-2 badge #{params[:type] == type.to_s ? 'badge-primary' : 'badge-outline'}" do
|
9
|
+
%>
|
10
|
+
<%= recurring_task_circle(type) %>
|
11
|
+
<%= type.to_s.titleize %>
|
12
|
+
<span class="opacity-50 -ml-0.5">
|
13
|
+
<%= SolidQueueDashboard.decorate(SolidQueue::RecurringTask.all).with_type(type).count %>
|
14
|
+
</span>
|
15
|
+
<% end %>
|
16
|
+
<% end %>
|
17
|
+
|
18
|
+
<% if params[:type].present? %>
|
19
|
+
<%= button_tag type: :submit, name: :type, value: nil, class: "badge badge-destructive gap-1" do %>
|
20
|
+
<%= icon_x class: "size-3.5 text-red-500" %>
|
21
|
+
Clear
|
22
|
+
<% end %>
|
23
|
+
<% end %>
|
24
|
+
</div>
|
25
|
+
</div>
|
26
|
+
<% end %>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
<div class="table-wrapper">
|
2
|
+
<table class="table">
|
3
|
+
<thead class="table-header">
|
4
|
+
<tr class="table-row">
|
5
|
+
<th class="table-head pl-6">ID</th>
|
6
|
+
<th class="table-head">Type</th>
|
7
|
+
<th class="table-head"></th>
|
8
|
+
<th class="table-head">Schedule</th>
|
9
|
+
<th class="table-head">Queue Name</th>
|
10
|
+
<th class="table-head">Priority</th>
|
11
|
+
<th class="table-head">Key</th>
|
12
|
+
<th class="table-head"></th>
|
13
|
+
</tr>
|
14
|
+
</thead>
|
15
|
+
<tbody class="table-body">
|
16
|
+
<%= render partial: 'solid_queue_dashboard/recurring_tasks/table_row', collection: recurring_tasks, as: :recurring_task %>
|
17
|
+
</tbody>
|
18
|
+
</table>
|
19
|
+
</div>
|
@@ -0,0 +1,31 @@
|
|
1
|
+
<tr class="table-row" data-href="<%= recurring_task_path(recurring_task) %>">
|
2
|
+
<td class="table-cell pl-6 font-medium text-zinc-900 dark:text-zinc-100">
|
3
|
+
<%= recurring_task.id %>
|
4
|
+
</td>
|
5
|
+
|
6
|
+
<td class="table-cell">
|
7
|
+
<%= recurring_task_type_badge(recurring_task.type) %>
|
8
|
+
</td>
|
9
|
+
|
10
|
+
<td class="table-cell">
|
11
|
+
<span class="font-medium">
|
12
|
+
<%= recurring_task.class_name || recurring_task.command %>
|
13
|
+
</span>
|
14
|
+
</td>
|
15
|
+
|
16
|
+
<td class="table-cell"><%= recurring_task.schedule %></td>
|
17
|
+
<td class="table-cell"><%= recurring_task.queue_name&.titleize || empty_value %></td>
|
18
|
+
<td class="table-cell"><%= recurring_task.priority || empty_value %></td>
|
19
|
+
<td class="table-cell"><%= recurring_task.key %></td>
|
20
|
+
<td class="table-cell">
|
21
|
+
<%= form_with url: enqueue_recurring_task_path(recurring_task), method: :post do %>
|
22
|
+
<button
|
23
|
+
type="submit"
|
24
|
+
class="btn btn-md btn-outline"
|
25
|
+
>
|
26
|
+
<%= icon_play class: "size-4 text-amber-500 mr-1" %>
|
27
|
+
Enqueue Now
|
28
|
+
</button>
|
29
|
+
<% end %>
|
30
|
+
</td>
|
31
|
+
</tr>
|