solid_queue_dashboard 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -0
  3. data/CHANGELOG.md +1 -1
  4. data/Procfile.dev +2 -0
  5. data/README.md +72 -21
  6. data/app/assets/javascripts/solid_queue_dashboard/alpine.js +5 -0
  7. data/app/assets/javascripts/solid_queue_dashboard/application.js +46 -0
  8. data/app/assets/stylesheets/solid_queue_dashboard/application.css +2062 -0
  9. data/app/assets/stylesheets/solid_queue_dashboard/tailwind.css +554 -0
  10. data/app/controllers/solid_queue_dashboard/appearance_controller.rb +8 -0
  11. data/app/controllers/solid_queue_dashboard/application_controller.rb +7 -0
  12. data/app/controllers/solid_queue_dashboard/dashboard_controller.rb +8 -0
  13. data/app/controllers/solid_queue_dashboard/jobs_controller.rb +42 -0
  14. data/app/controllers/solid_queue_dashboard/processes_controller.rb +28 -0
  15. data/app/controllers/solid_queue_dashboard/recurring_tasks_controller.rb +30 -0
  16. data/app/helpers/solid_queue_dashboard/appearance_helper.rb +7 -0
  17. data/app/helpers/solid_queue_dashboard/application_helper.rb +7 -0
  18. data/app/helpers/solid_queue_dashboard/icons_helper.rb +231 -0
  19. data/app/helpers/solid_queue_dashboard/jobs_helper.rb +51 -0
  20. data/app/helpers/solid_queue_dashboard/pagination_helper.rb +38 -0
  21. data/app/helpers/solid_queue_dashboard/processes_helper.rb +35 -0
  22. data/app/helpers/solid_queue_dashboard/recurring_tasks_helper.rb +33 -0
  23. data/app/views/layouts/solid_queue_dashboard/application.html.erb +21 -0
  24. data/app/views/solid_queue_dashboard/application/_flash_messages.html.erb +38 -0
  25. data/app/views/solid_queue_dashboard/application/_footer.html.erb +11 -0
  26. data/app/views/solid_queue_dashboard/application/_navbar.html.erb +50 -0
  27. data/app/views/solid_queue_dashboard/application/_pagination.html.erb +19 -0
  28. data/app/views/solid_queue_dashboard/dashboard/index.html.erb +41 -0
  29. data/app/views/solid_queue_dashboard/jobs/_filters.html.erb +87 -0
  30. data/app/views/solid_queue_dashboard/jobs/_table.html.erb +19 -0
  31. data/app/views/solid_queue_dashboard/jobs/_table_row.html.erb +82 -0
  32. data/app/views/solid_queue_dashboard/jobs/index.html.erb +57 -0
  33. data/app/views/solid_queue_dashboard/jobs/show.html.erb +192 -0
  34. data/app/views/solid_queue_dashboard/processes/_filters.html.erb +64 -0
  35. data/app/views/solid_queue_dashboard/processes/_table.html.erb +18 -0
  36. data/app/views/solid_queue_dashboard/processes/_table_row.html.erb +32 -0
  37. data/app/views/solid_queue_dashboard/processes/index.html.erb +27 -0
  38. data/app/views/solid_queue_dashboard/processes/show.html.erb +79 -0
  39. data/app/views/solid_queue_dashboard/recurring_tasks/_filters.html.erb +26 -0
  40. data/app/views/solid_queue_dashboard/recurring_tasks/_table.html.erb +19 -0
  41. data/app/views/solid_queue_dashboard/recurring_tasks/_table_row.html.erb +31 -0
  42. data/app/views/solid_queue_dashboard/recurring_tasks/index.html.erb +21 -0
  43. data/app/views/solid_queue_dashboard/recurring_tasks/show.html.erb +129 -0
  44. data/bun.lockb +0 -0
  45. data/config/routes.rb +18 -0
  46. data/lib/solid_queue_dashboard/configuration.rb +17 -0
  47. data/lib/solid_queue_dashboard/decorators/job_decorator.rb +63 -0
  48. data/lib/solid_queue_dashboard/decorators/jobs_decorator.rb +74 -0
  49. data/lib/solid_queue_dashboard/decorators/process_decorator.rb +13 -0
  50. data/lib/solid_queue_dashboard/decorators/processes_decorator.rb +15 -0
  51. data/lib/solid_queue_dashboard/decorators/recurring_task_decorator.rb +22 -0
  52. data/lib/solid_queue_dashboard/decorators/recurring_tasks_decorator.rb +26 -0
  53. data/lib/solid_queue_dashboard/engine.rb +13 -0
  54. data/lib/solid_queue_dashboard/job.rb +26 -0
  55. data/lib/solid_queue_dashboard/process.rb +24 -0
  56. data/lib/solid_queue_dashboard/recurring_task.rb +14 -0
  57. data/lib/solid_queue_dashboard/version.rb +1 -1
  58. data/lib/solid_queue_dashboard.rb +39 -1
  59. data/package.json +13 -0
  60. data/tailwind.config.js +83 -0
  61. 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>