solid_stack_web 0.5.0 → 0.7.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/README.md +12 -2
- data/app/assets/stylesheets/solid_stack_web/_01_base.css +3 -2
- data/app/assets/stylesheets/solid_stack_web/_02_layout.css +28 -0
- data/app/assets/stylesheets/solid_stack_web/_04_table.css +13 -1
- data/app/assets/stylesheets/solid_stack_web/_07_dashboard.css +49 -1
- data/app/assets/stylesheets/solid_stack_web/_08_filters.css +6 -0
- data/app/assets/stylesheets/solid_stack_web/_10_responsive.css +54 -0
- data/app/assets/stylesheets/solid_stack_web/_12_dark_mode.css +34 -0
- data/app/controllers/solid_stack_web/cable/channel_purges_controller.rb +8 -0
- data/app/controllers/solid_stack_web/cable/purges_controller.rb +9 -0
- data/app/controllers/solid_stack_web/cable_controller.rb +20 -1
- data/app/controllers/solid_stack_web/cable_messages_controller.rb +13 -0
- data/app/controllers/solid_stack_web/failed_jobs/selections_controller.rb +4 -3
- data/app/controllers/solid_stack_web/failed_jobs_controller.rb +2 -1
- data/app/controllers/solid_stack_web/jobs/selections_controller.rb +2 -2
- data/app/controllers/solid_stack_web/jobs_controller.rb +4 -2
- data/app/helpers/solid_stack_web/application_helper.rb +25 -0
- data/app/javascript/solid_stack_web/application.js +5 -1
- data/app/javascript/solid_stack_web/theme_controller.js +26 -0
- data/app/javascript/solid_stack_web/timestamp_controller.js +59 -0
- data/app/models/solid_stack_web/cable_stats.rb +15 -2
- data/app/models/solid_stack_web/cable_timeline.rb +32 -0
- data/app/views/layouts/solid_stack_web/application.html.erb +18 -4
- data/app/views/solid_stack_web/cable/index.html.erb +56 -3
- data/app/views/solid_stack_web/cable_messages/index.html.erb +56 -0
- data/app/views/solid_stack_web/cache_entries/index.html.erb +9 -3
- data/app/views/solid_stack_web/cache_entries/show.html.erb +1 -1
- data/app/views/solid_stack_web/dashboard/index.html.erb +25 -4
- data/app/views/solid_stack_web/failed_jobs/destroy.turbo_stream.erb +3 -0
- data/app/views/solid_stack_web/failed_jobs/index.html.erb +4 -3
- data/app/views/solid_stack_web/failed_jobs/show.html.erb +1 -1
- data/app/views/solid_stack_web/history/index.html.erb +9 -3
- data/app/views/solid_stack_web/jobs/_empty.html.erb +19 -2
- data/app/views/solid_stack_web/jobs/destroy.turbo_stream.erb +3 -0
- data/app/views/solid_stack_web/jobs/index.html.erb +3 -3
- data/app/views/solid_stack_web/jobs/show.html.erb +4 -4
- data/app/views/solid_stack_web/processes/index.html.erb +3 -2
- data/app/views/solid_stack_web/queues/index.html.erb +2 -1
- data/app/views/solid_stack_web/queues/show.html.erb +2 -2
- data/app/views/solid_stack_web/recurring_tasks/index.html.erb +5 -5
- data/app/views/solid_stack_web/scheduled_jobs/update.turbo_stream.erb +1 -1
- data/app/views/solid_stack_web/shared/_flash.html.erb +1 -0
- data/app/views/solid_stack_web/stats/index.html.erb +2 -1
- data/config/importmap.rb +2 -0
- data/config/routes.rb +4 -1
- data/lib/solid_stack_web/version.rb +1 -1
- metadata +11 -1
|
@@ -4,12 +4,13 @@
|
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
6
|
<title>Solid Stack Dashboard</title>
|
|
7
|
+
<link rel="icon" href="data:,">
|
|
7
8
|
<%= csrf_meta_tags %>
|
|
8
9
|
<%= csp_meta_tag %>
|
|
9
10
|
<%= inline_styles %>
|
|
10
11
|
<%= javascript_importmap_tags "solid_stack_web" %>
|
|
11
12
|
</head>
|
|
12
|
-
<body>
|
|
13
|
+
<body data-controller="theme">
|
|
13
14
|
<header class="sqw-header">
|
|
14
15
|
<div class="sqw-header__inner">
|
|
15
16
|
<%= link_to "Solid Stack", root_path, class: "sqw-header__logo" %>
|
|
@@ -21,6 +22,8 @@
|
|
|
21
22
|
<%= link_to "Cable", cable_path,
|
|
22
23
|
class: "sqw-nav__link#{" sqw-nav__link--active" if current_section == :cable}" %>
|
|
23
24
|
</nav>
|
|
25
|
+
<button class="sqw-theme-toggle" aria-label="Switch to dark mode"
|
|
26
|
+
data-theme-target="toggle" data-action="theme#toggle">☽</button>
|
|
24
27
|
</div>
|
|
25
28
|
</header>
|
|
26
29
|
|
|
@@ -35,6 +38,15 @@
|
|
|
35
38
|
</nav>
|
|
36
39
|
<% end %>
|
|
37
40
|
|
|
41
|
+
<% if current_section == :cable %>
|
|
42
|
+
<nav class="sqw-subnav">
|
|
43
|
+
<div class="sqw-subnav__inner">
|
|
44
|
+
<%= link_to "Overview", cable_path,
|
|
45
|
+
class: "sqw-subnav__link#{" sqw-subnav__link--active" if controller_name == "cable"}" %>
|
|
46
|
+
</div>
|
|
47
|
+
</nav>
|
|
48
|
+
<% end %>
|
|
49
|
+
|
|
38
50
|
<% if current_section == :queue %>
|
|
39
51
|
<nav class="sqw-subnav">
|
|
40
52
|
<div class="sqw-subnav__inner">
|
|
@@ -57,9 +69,11 @@
|
|
|
57
69
|
<% end %>
|
|
58
70
|
|
|
59
71
|
<main class="sqw-main">
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
72
|
+
<div id="sqw-flash-container">
|
|
73
|
+
<% flash.each do |type, message| %>
|
|
74
|
+
<div class="sqw-flash sqw-flash--<%= type %>"><%= message %></div>
|
|
75
|
+
<% end %>
|
|
76
|
+
</div>
|
|
63
77
|
<%= yield %>
|
|
64
78
|
</main>
|
|
65
79
|
</body>
|
|
@@ -1,5 +1,14 @@
|
|
|
1
|
-
<div class="sqw-page-header">
|
|
1
|
+
<div class="sqw-page-header sqw-page-header--split">
|
|
2
2
|
<h1 class="sqw-page-title">Solid Cable</h1>
|
|
3
|
+
<div class="sqw-header-actions">
|
|
4
|
+
<%= form_with url: cable_purge_path, method: :delete, class: "sqw-inline-form",
|
|
5
|
+
data: { turbo_confirm: "Purge these messages? This cannot be undone." } do |f| %>
|
|
6
|
+
<%= f.select :older_than,
|
|
7
|
+
[["Older than 1 day", 1], ["Older than 7 days", 7], ["Older than 30 days", 30]],
|
|
8
|
+
{}, class: "sqw-select" %>
|
|
9
|
+
<%= f.submit "Purge Old", class: "sqw-btn sqw-btn--danger sqw-btn--sm" %>
|
|
10
|
+
<% end %>
|
|
11
|
+
</div>
|
|
3
12
|
</div>
|
|
4
13
|
|
|
5
14
|
<div class="sqw-stats-grid">
|
|
@@ -13,15 +22,59 @@
|
|
|
13
22
|
</div>
|
|
14
23
|
</div>
|
|
15
24
|
|
|
25
|
+
<div class="sqw-timeline-grid sqw-timeline-grid--single" data-controller="sparkline-tooltip">
|
|
26
|
+
<div class="sqw-sparkline-wrap sqw-timeline-chart sqw-timeline-chart--cable">
|
|
27
|
+
<span class="sqw-sparkline-label">Messages — last 24 hours</span>
|
|
28
|
+
<div class="sqw-sparkline-positioner">
|
|
29
|
+
<%= cable_messages_timeline_svg(@timeline) %>
|
|
30
|
+
<div class="sqw-sparkline-tip" data-sparkline-tooltip-target="tip" hidden></div>
|
|
31
|
+
</div>
|
|
32
|
+
<div class="sqw-sparkline-axis">
|
|
33
|
+
<span>24h ago</span>
|
|
34
|
+
<span>12h ago</span>
|
|
35
|
+
<span>now</span>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<form class="sqw-filters" action="<%= cable_path %>" method="get" data-controller="search">
|
|
41
|
+
<input class="sqw-search-input" type="search" name="q" value="<%= @search %>"
|
|
42
|
+
placeholder="Filter by channel…" autocomplete="off" aria-label="Filter by channel"
|
|
43
|
+
data-action="input->search#filter">
|
|
44
|
+
<% if @search.present? %>
|
|
45
|
+
<%= link_to "Clear", cable_path, class: "sqw-btn sqw-btn--muted sqw-btn--sm" %>
|
|
46
|
+
<% end %>
|
|
47
|
+
</form>
|
|
48
|
+
|
|
16
49
|
<% if @channels.any? %>
|
|
17
50
|
<table class="sqw-table">
|
|
18
51
|
<thead>
|
|
19
|
-
<tr
|
|
52
|
+
<tr>
|
|
53
|
+
<th>Channel</th>
|
|
54
|
+
<th>Messages</th>
|
|
55
|
+
<th>Last Message</th>
|
|
56
|
+
</tr>
|
|
20
57
|
</thead>
|
|
21
58
|
<tbody>
|
|
22
59
|
<% @channels.each do |channel| %>
|
|
23
|
-
<tr
|
|
60
|
+
<tr>
|
|
61
|
+
<td class="sqw-monospace sqw-truncate" title="<%= channel[:channel] %>">
|
|
62
|
+
<%= link_to channel[:channel], cable_channel_messages_path(channel[:channel_hash]), class: "sqw-link" %>
|
|
63
|
+
</td>
|
|
64
|
+
<td><%= channel[:message_count] %></td>
|
|
65
|
+
<td class="sqw-muted"><%= local_time(channel[:last_message_at]) %></td>
|
|
66
|
+
</tr>
|
|
24
67
|
<% end %>
|
|
25
68
|
</tbody>
|
|
26
69
|
</table>
|
|
70
|
+
<% else %>
|
|
71
|
+
<div class="sqw-empty">
|
|
72
|
+
<% if @search.present? %>
|
|
73
|
+
<p class="sqw-empty__title">No channels matching “<%= @search %>”</p>
|
|
74
|
+
<p class="sqw-empty__hint"><%= link_to "Clear search", cable_path %></p>
|
|
75
|
+
<% else %>
|
|
76
|
+
<p class="sqw-empty__title">No cable messages</p>
|
|
77
|
+
<p class="sqw-empty__hint">Messages will appear here once clients connect and broadcast over Action Cable.</p>
|
|
78
|
+
<% end %>
|
|
79
|
+
</div>
|
|
27
80
|
<% end %>
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<div class="sqw-page-header sqw-page-header--split">
|
|
2
|
+
<h1 class="sqw-page-title sqw-truncate" title="<%= @channel_name %>"><%= @channel_name %></h1>
|
|
3
|
+
<div class="sqw-header-actions">
|
|
4
|
+
<%= button_to "Purge Channel",
|
|
5
|
+
cable_channel_purge_path(params[:channel_hash]),
|
|
6
|
+
method: :delete,
|
|
7
|
+
class: "sqw-btn sqw-btn--danger sqw-btn--sm",
|
|
8
|
+
data: { turbo_confirm: "Delete all messages for this channel? This cannot be undone." } %>
|
|
9
|
+
<%= link_to "← Channels", cable_path, class: "sqw-btn sqw-btn--muted sqw-btn--sm" %>
|
|
10
|
+
</div>
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
<form class="sqw-filters" action="<%= cable_channel_messages_path(params[:channel_hash]) %>" method="get" data-controller="search">
|
|
14
|
+
<input class="sqw-search-input" type="search" name="q" value="<%= @search %>"
|
|
15
|
+
placeholder="Filter by payload…" autocomplete="off" aria-label="Filter by payload"
|
|
16
|
+
data-action="input->search#filter">
|
|
17
|
+
<% if @search.present? %>
|
|
18
|
+
<%= link_to "Clear", cable_channel_messages_path(params[:channel_hash]), class: "sqw-btn sqw-btn--muted sqw-btn--sm" %>
|
|
19
|
+
<% end %>
|
|
20
|
+
</form>
|
|
21
|
+
|
|
22
|
+
<% if @messages.any? %>
|
|
23
|
+
<table class="sqw-table">
|
|
24
|
+
<thead>
|
|
25
|
+
<tr>
|
|
26
|
+
<th>ID</th>
|
|
27
|
+
<th>Payload</th>
|
|
28
|
+
<th>Sent</th>
|
|
29
|
+
</tr>
|
|
30
|
+
</thead>
|
|
31
|
+
<tbody>
|
|
32
|
+
<% @messages.each do |message| %>
|
|
33
|
+
<tr>
|
|
34
|
+
<td class="sqw-muted sqw-monospace"><%= message.id %></td>
|
|
35
|
+
<td class="sqw-monospace sqw-truncate" title="<%= message.payload %>">
|
|
36
|
+
<%= truncate(message.payload.to_s, length: 120) %>
|
|
37
|
+
</td>
|
|
38
|
+
<td class="sqw-muted">
|
|
39
|
+
<%= local_time(message.created_at, format: :relative) %>
|
|
40
|
+
</td>
|
|
41
|
+
</tr>
|
|
42
|
+
<% end %>
|
|
43
|
+
</tbody>
|
|
44
|
+
</table>
|
|
45
|
+
<%== @pagy.series_nav if @pagy.pages > 1 %>
|
|
46
|
+
<% else %>
|
|
47
|
+
<div class="sqw-empty">
|
|
48
|
+
<% if @search.present? %>
|
|
49
|
+
<p class="sqw-empty__title">No messages matching “<%= @search %>”</p>
|
|
50
|
+
<p class="sqw-empty__hint"><%= link_to "Clear search", cable_channel_messages_path(params[:channel_hash]) %></p>
|
|
51
|
+
<% else %>
|
|
52
|
+
<p class="sqw-empty__title">No messages for this channel</p>
|
|
53
|
+
<p class="sqw-empty__hint">Messages may have been purged or the channel has gone quiet. <%= link_to "Back to channels →", cable_path %></p>
|
|
54
|
+
<% end %>
|
|
55
|
+
</div>
|
|
56
|
+
<% end %>
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
<%= link_to entry.key, cache_entry_path(entry), class: "sqw-link" %>
|
|
47
47
|
</td>
|
|
48
48
|
<td><%= number_to_human_size(entry.byte_size) %></td>
|
|
49
|
-
<td class="sqw-muted"><%= entry.created_at
|
|
49
|
+
<td class="sqw-muted"><%= local_time(entry.created_at) %></td>
|
|
50
50
|
<td class="sqw-actions">
|
|
51
51
|
<%= button_to "Delete",
|
|
52
52
|
cache_entry_path(entry, q: @search, column: @sort["column"], direction: @sort["direction"]),
|
|
@@ -58,9 +58,15 @@
|
|
|
58
58
|
<% end %>
|
|
59
59
|
</tbody>
|
|
60
60
|
</table>
|
|
61
|
-
<%==
|
|
61
|
+
<%== @pagy.series_nav if @pagy.pages > 1 %>
|
|
62
62
|
<% else %>
|
|
63
63
|
<div class="sqw-empty">
|
|
64
|
-
|
|
64
|
+
<% if @search.present? %>
|
|
65
|
+
<p class="sqw-empty__title">No entries matching “<%= @search %>”</p>
|
|
66
|
+
<p class="sqw-empty__hint"><%= link_to "Clear search", cache_entries_path %></p>
|
|
67
|
+
<% else %>
|
|
68
|
+
<p class="sqw-empty__title">No cache entries</p>
|
|
69
|
+
<p class="sqw-empty__hint">Entries will appear here once your application writes to the cache.</p>
|
|
70
|
+
<% end %>
|
|
65
71
|
</div>
|
|
66
72
|
<% end %>
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
<% if @cache_stats[:oldest_entry] %>
|
|
88
88
|
<div class="sqw-inline-stat sqw-inline-stat--cache">
|
|
89
89
|
<span class="sqw-inline-stat__label">Oldest</span>
|
|
90
|
-
<span class="sqw-inline-stat__value sqw-inline-stat__value--sm"
|
|
90
|
+
<span class="sqw-inline-stat__value sqw-inline-stat__value--sm"><%= local_time(@cache_stats[:oldest_entry], format: :relative) %></span>
|
|
91
91
|
</div>
|
|
92
92
|
<% end %>
|
|
93
93
|
</div>
|
|
@@ -99,15 +99,36 @@
|
|
|
99
99
|
<%= link_to "View Cable →", cable_path, class: "sqw-gem-card__link" %>
|
|
100
100
|
</div>
|
|
101
101
|
<div class="sqw-gem-card__body">
|
|
102
|
-
|
|
102
|
+
<%= link_to cable_path, class: "sqw-inline-stat sqw-inline-stat--cable" do %>
|
|
103
103
|
<span class="sqw-inline-stat__label">Messages</span>
|
|
104
104
|
<span class="sqw-inline-stat__value"><%= @cable_stats[:messages] %></span>
|
|
105
|
-
|
|
105
|
+
<% end %>
|
|
106
106
|
<div class="sqw-inline-stat sqw-inline-stat--cable">
|
|
107
107
|
<span class="sqw-inline-stat__label">Channels</span>
|
|
108
108
|
<span class="sqw-inline-stat__value"><%= @cable_stats[:channels] %></span>
|
|
109
109
|
</div>
|
|
110
|
+
<div class="sqw-inline-stat sqw-inline-stat--cable">
|
|
111
|
+
<span class="sqw-inline-stat__label">Msg/hr</span>
|
|
112
|
+
<span class="sqw-inline-stat__value"><%= @cable_stats[:messages_per_hour] %></span>
|
|
113
|
+
</div>
|
|
114
|
+
<% if @cable_stats[:oldest_message] %>
|
|
115
|
+
<div class="sqw-inline-stat sqw-inline-stat--cable">
|
|
116
|
+
<span class="sqw-inline-stat__label">Oldest</span>
|
|
117
|
+
<span class="sqw-inline-stat__value sqw-inline-stat__value--sm"><%= local_time(@cable_stats[:oldest_message], format: :relative) %></span>
|
|
118
|
+
</div>
|
|
119
|
+
<% end %>
|
|
110
120
|
</div>
|
|
121
|
+
<% if @cable_stats[:top_channels].any? %>
|
|
122
|
+
<div class="sqw-card-list">
|
|
123
|
+
<span class="sqw-card-list__label">Top channels</span>
|
|
124
|
+
<% @cable_stats[:top_channels].each do |channel, count| %>
|
|
125
|
+
<div class="sqw-card-list__row">
|
|
126
|
+
<span class="sqw-card-list__name" title="<%= channel %>"><%= channel %></span>
|
|
127
|
+
<span class="sqw-card-list__count"><%= count %></span>
|
|
128
|
+
</div>
|
|
129
|
+
<% end %>
|
|
130
|
+
</div>
|
|
131
|
+
<% end %>
|
|
111
132
|
</div>
|
|
112
133
|
</div>
|
|
113
|
-
<% end %>
|
|
134
|
+
<% end %>
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
<td class="sqw-monospace"><%= link_to execution.job.class_name, failed_job_path(execution) %></td>
|
|
51
51
|
<td><span class="sqw-badge sqw-badge--queue"><%= execution.job.queue_name %></span></td>
|
|
52
52
|
<td class="sqw-muted sqw-truncate" title="<%= execution.exception_class %>: <%= execution.message %>"><%= execution.exception_class %></td>
|
|
53
|
-
<td class="sqw-muted"><%= execution.created_at
|
|
53
|
+
<td class="sqw-muted"><%= local_time(execution.created_at) %></td>
|
|
54
54
|
<td class="sqw-actions">
|
|
55
55
|
<%= button_to "Retry", retry_failed_job_path(execution),
|
|
56
56
|
method: :post, class: "sqw-btn sqw-btn--sm" %>
|
|
@@ -62,11 +62,12 @@
|
|
|
62
62
|
<% end %>
|
|
63
63
|
</tbody>
|
|
64
64
|
</table>
|
|
65
|
-
<%==
|
|
65
|
+
<%== @pagy.series_nav if @pagy.pages > 1 %>
|
|
66
66
|
</div>
|
|
67
67
|
<% else %>
|
|
68
68
|
<div class="sqw-empty">
|
|
69
|
-
<p>No failed jobs
|
|
69
|
+
<p class="sqw-empty__title">No failed jobs</p>
|
|
70
|
+
<p class="sqw-empty__hint">All clear — your jobs are running without errors.</p>
|
|
70
71
|
</div>
|
|
71
72
|
<% end %>
|
|
72
73
|
</div>
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
<dd class="sqw-monospace sqw-truncate" title="<%= @execution.job.active_job_id %>"><%= @execution.job.active_job_id.presence || "—" %></dd>
|
|
30
30
|
|
|
31
31
|
<dt>Failed At</dt>
|
|
32
|
-
<dd class="sqw-monospace"><%= @execution.created_at
|
|
32
|
+
<dd class="sqw-monospace"><%= local_time(@execution.created_at, format: :long) %></dd>
|
|
33
33
|
|
|
34
34
|
<dt>Error</dt>
|
|
35
35
|
<dd class="sqw-monospace"><%= @execution.exception_class %></dd>
|
|
@@ -61,16 +61,22 @@
|
|
|
61
61
|
class: "sqw-badge sqw-badge--queue" %>
|
|
62
62
|
</td>
|
|
63
63
|
<td class="sqw-monospace"><%= format_duration(job.finished_at - job.created_at) %></td>
|
|
64
|
-
<td class="sqw-muted"><%= job.finished_at
|
|
64
|
+
<td class="sqw-muted"><%= local_time(job.finished_at) %></td>
|
|
65
65
|
</tr>
|
|
66
66
|
<% end %>
|
|
67
67
|
</tbody>
|
|
68
68
|
</table>
|
|
69
|
-
<%==
|
|
69
|
+
<%== @pagy.series_nav if @pagy.pages > 1 %>
|
|
70
70
|
</div>
|
|
71
71
|
<% else %>
|
|
72
72
|
<div class="sqw-empty">
|
|
73
|
-
|
|
73
|
+
<% if @search.present? || @queue.present? || @period.present? %>
|
|
74
|
+
<p class="sqw-empty__title">No finished jobs match your filters</p>
|
|
75
|
+
<p class="sqw-empty__hint"><%= link_to "Clear filters", history_path %></p>
|
|
76
|
+
<% else %>
|
|
77
|
+
<p class="sqw-empty__title">No finished jobs yet</p>
|
|
78
|
+
<p class="sqw-empty__hint">Completed jobs will appear here once workers process them.</p>
|
|
79
|
+
<% end %>
|
|
74
80
|
</div>
|
|
75
81
|
<% end %>
|
|
76
82
|
<% end %>
|
|
@@ -1,3 +1,20 @@
|
|
|
1
1
|
<div id="sqw-empty" class="sqw-empty">
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
<% if @search.present? %>
|
|
3
|
+
<p class="sqw-empty__title">No <strong><%= @status %></strong> jobs matching “<%= @search %>”</p>
|
|
4
|
+
<p class="sqw-empty__hint"><%= link_to "Clear search", jobs_path(status: @status) %></p>
|
|
5
|
+
<% else %>
|
|
6
|
+
<p class="sqw-empty__title">No <strong><%= @status %></strong> jobs</p>
|
|
7
|
+
<p class="sqw-empty__hint">
|
|
8
|
+
<% case @status %>
|
|
9
|
+
<% when "ready" %>
|
|
10
|
+
Jobs will appear here once they are enqueued and ready to be picked up.
|
|
11
|
+
<% when "scheduled" %>
|
|
12
|
+
No jobs are scheduled to run in the future.
|
|
13
|
+
<% when "claimed" %>
|
|
14
|
+
No jobs are currently being processed by a worker.
|
|
15
|
+
<% when "blocked" %>
|
|
16
|
+
No jobs are blocked by concurrency controls.
|
|
17
|
+
<% end %>
|
|
18
|
+
</p>
|
|
19
|
+
<% end %>
|
|
20
|
+
</div>
|
|
@@ -121,9 +121,9 @@
|
|
|
121
121
|
<td class="sqw-monospace"><%= link_to execution.job.class_name, job_path(execution.id, status: @status), data: { turbo_frame: "_top" } %></td>
|
|
122
122
|
<td><span class="sqw-badge sqw-badge--queue"><%= execution.job.queue_name %></span></td>
|
|
123
123
|
<td><%= execution.job.priority %></td>
|
|
124
|
-
<td class="sqw-muted"><%= execution.created_at
|
|
124
|
+
<td class="sqw-muted"><%= local_time(execution.created_at) %></td>
|
|
125
125
|
<% if @status == "scheduled" %>
|
|
126
|
-
<td id="scheduled_at_<%= execution.id %>" class="sqw-muted"><%= execution.scheduled_at
|
|
126
|
+
<td id="scheduled_at_<%= execution.id %>" class="sqw-muted"><%= local_time(execution.scheduled_at) %></td>
|
|
127
127
|
<% end %>
|
|
128
128
|
<td class="sqw-actions">
|
|
129
129
|
<% if @status == "scheduled" %>
|
|
@@ -149,7 +149,7 @@
|
|
|
149
149
|
<% end %>
|
|
150
150
|
</tbody>
|
|
151
151
|
</table>
|
|
152
|
-
<%==
|
|
152
|
+
<%== @pagy.series_nav if @pagy.pages > 1 %>
|
|
153
153
|
</div>
|
|
154
154
|
<% else %>
|
|
155
155
|
<%= render "empty" %>
|
|
@@ -36,17 +36,17 @@
|
|
|
36
36
|
|
|
37
37
|
<% if @status == "blocked" %>
|
|
38
38
|
<dt>Blocked Until</dt>
|
|
39
|
-
<dd class="sqw-monospace"><%= @execution.expires_at
|
|
39
|
+
<dd class="sqw-monospace"><%= local_time(@execution.expires_at, format: :long) %></dd>
|
|
40
40
|
<% end %>
|
|
41
41
|
|
|
42
42
|
<dt>Enqueued At</dt>
|
|
43
|
-
<dd class="sqw-monospace"><%= @execution.job.created_at
|
|
43
|
+
<dd class="sqw-monospace"><%= local_time(@execution.job.created_at, format: :long) %></dd>
|
|
44
44
|
|
|
45
45
|
<dt>Scheduled At</dt>
|
|
46
|
-
<dd class="sqw-monospace"><%= @execution.job.scheduled_at
|
|
46
|
+
<dd class="sqw-monospace"><%= local_time(@execution.job.scheduled_at, format: :long) %></dd>
|
|
47
47
|
|
|
48
48
|
<dt>Finished At</dt>
|
|
49
|
-
<dd class="sqw-monospace"><%= @execution.job.finished_at
|
|
49
|
+
<dd class="sqw-monospace"><%= local_time(@execution.job.finished_at, format: :long) %></dd>
|
|
50
50
|
</dl>
|
|
51
51
|
</div>
|
|
52
52
|
|
|
@@ -22,14 +22,15 @@
|
|
|
22
22
|
<td class="sqw-monospace"><%= process.name %></td>
|
|
23
23
|
<td class="sqw-monospace"><%= process.pid %></td>
|
|
24
24
|
<td class="sqw-muted"><%= process.hostname %></td>
|
|
25
|
-
<td class="sqw-muted"><%= process.last_heartbeat_at
|
|
25
|
+
<td class="sqw-muted"><%= local_time(process.last_heartbeat_at) %></td>
|
|
26
26
|
</tr>
|
|
27
27
|
<% end %>
|
|
28
28
|
</tbody>
|
|
29
29
|
</table>
|
|
30
30
|
<% else %>
|
|
31
31
|
<div class="sqw-empty">
|
|
32
|
-
<p>No active processes
|
|
32
|
+
<p class="sqw-empty__title">No active processes</p>
|
|
33
|
+
<p class="sqw-empty__hint">Start a Solid Queue worker to begin processing jobs.</p>
|
|
33
34
|
</div>
|
|
34
35
|
<% end %>
|
|
35
36
|
<% end %>
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
</table>
|
|
45
45
|
<% else %>
|
|
46
46
|
<div class="sqw-empty">
|
|
47
|
-
<p>No queues with ready jobs
|
|
47
|
+
<p class="sqw-empty__title">No queues with ready jobs</p>
|
|
48
|
+
<p class="sqw-empty__hint">Workers are idle or all jobs are in another state. <%= link_to "Check job statuses →", jobs_path %></p>
|
|
48
49
|
</div>
|
|
49
50
|
<% end %>
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
data: { turbo_frame: "_top" } %>
|
|
50
50
|
</td>
|
|
51
51
|
<td><%= execution.job.priority %></td>
|
|
52
|
-
<td class="sqw-muted"><%= execution.created_at
|
|
52
|
+
<td class="sqw-muted"><%= local_time(execution.created_at) %></td>
|
|
53
53
|
<td class="sqw-actions">
|
|
54
54
|
<%= button_to "Discard", job_path(execution, status: "ready", queue: @queue_name),
|
|
55
55
|
method: :delete, class: "sqw-btn sqw-btn--danger sqw-btn--sm",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
<% end %>
|
|
60
60
|
</tbody>
|
|
61
61
|
</table>
|
|
62
|
-
<%==
|
|
62
|
+
<%== @pagy.series_nav if @pagy.pages > 1 %>
|
|
63
63
|
<% else %>
|
|
64
64
|
<div class="sqw-empty">
|
|
65
65
|
<p>No ready jobs in <strong><%= @queue_name %></strong>.</p>
|
|
@@ -36,12 +36,11 @@
|
|
|
36
36
|
</td>
|
|
37
37
|
<td><span class="sqw-badge sqw-badge--queue"><%= task.queue_name.presence || "default" %></span></td>
|
|
38
38
|
<td class="sqw-muted sqw-monospace">
|
|
39
|
-
<% next_run = begin; task.next_time
|
|
40
|
-
<%= next_run
|
|
39
|
+
<% next_run = begin; task.next_time; rescue; nil; end %>
|
|
40
|
+
<%= local_time(next_run) %>
|
|
41
41
|
</td>
|
|
42
42
|
<td class="sqw-muted sqw-monospace">
|
|
43
|
-
|
|
44
|
-
<%= last_run ? last_run.strftime("%b %d %H:%M") : "—" %>
|
|
43
|
+
<%= local_time(task.last_enqueued_time) %>
|
|
45
44
|
</td>
|
|
46
45
|
<td>
|
|
47
46
|
<% if task.static? %>
|
|
@@ -62,6 +61,7 @@
|
|
|
62
61
|
</table>
|
|
63
62
|
<% else %>
|
|
64
63
|
<div class="sqw-empty">
|
|
65
|
-
<p>No recurring tasks configured
|
|
64
|
+
<p class="sqw-empty__title">No recurring tasks configured</p>
|
|
65
|
+
<p class="sqw-empty__hint">Define recurring tasks in your Solid Queue configuration to see them here.</p>
|
|
66
66
|
</div>
|
|
67
67
|
<% end %>
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<% else %>
|
|
4
4
|
<%= turbo_stream.replace "scheduled_at_#{@execution.id}" do %>
|
|
5
5
|
<td id="scheduled_at_<%= @execution.id %>" class="sqw-muted">
|
|
6
|
-
<%= @execution.scheduled_at
|
|
6
|
+
<%= local_time(@execution.scheduled_at) %>
|
|
7
7
|
</td>
|
|
8
8
|
<% end %>
|
|
9
9
|
<% end %>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<div class="sqw-flash sqw-flash--<%= type %>"><%= message %></div>
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
</table>
|
|
44
44
|
<% else %>
|
|
45
45
|
<div class="sqw-empty">
|
|
46
|
-
<p>No finished jobs yet
|
|
46
|
+
<p class="sqw-empty__title">No finished jobs yet</p>
|
|
47
|
+
<p class="sqw-empty__hint">Performance stats appear here once jobs complete. <%= link_to "View active jobs →", jobs_path %></p>
|
|
47
48
|
</div>
|
|
48
49
|
<% end %>
|
data/config/importmap.rb
CHANGED
|
@@ -5,3 +5,5 @@ pin "solid_stack_web/refresh_controller", to: "solid_stack_web/refresh_controlle
|
|
|
5
5
|
pin "solid_stack_web/search_controller", to: "solid_stack_web/search_controller.js"
|
|
6
6
|
pin "solid_stack_web/selection_controller", to: "solid_stack_web/selection_controller.js"
|
|
7
7
|
pin "solid_stack_web/sparkline_tooltip_controller", to: "solid_stack_web/sparkline_tooltip_controller.js"
|
|
8
|
+
pin "solid_stack_web/theme_controller", to: "solid_stack_web/theme_controller.js"
|
|
9
|
+
pin "solid_stack_web/timestamp_controller", to: "solid_stack_web/timestamp_controller.js"
|
data/config/routes.rb
CHANGED
|
@@ -37,5 +37,8 @@ SolidStackWeb::Engine.routes.draw do
|
|
|
37
37
|
get "cache", to: "cache#index", as: :cache
|
|
38
38
|
resources :cache_entries, only: [:index, :show, :destroy], path: "cache/entries"
|
|
39
39
|
resource :cache_flush, only: [:destroy], path: "cache/flush", controller: "cache/flushes"
|
|
40
|
-
get
|
|
40
|
+
get "cable", to: "cable#index", as: :cable
|
|
41
|
+
delete "cable/purge", to: "cable/purges#destroy", as: :cable_purge
|
|
42
|
+
get "cable/channels/:channel_hash", to: "cable_messages#index", as: :cable_channel_messages
|
|
43
|
+
delete "cable/channels/:channel_hash/purge", to: "cable/channel_purges#destroy", as: :cable_channel_purge
|
|
41
44
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: solid_stack_web
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Chuck Smith
|
|
@@ -142,9 +142,14 @@ files:
|
|
|
142
142
|
- app/assets/stylesheets/solid_stack_web/_07_dashboard.css
|
|
143
143
|
- app/assets/stylesheets/solid_stack_web/_08_filters.css
|
|
144
144
|
- app/assets/stylesheets/solid_stack_web/_09_detail.css
|
|
145
|
+
- app/assets/stylesheets/solid_stack_web/_10_responsive.css
|
|
146
|
+
- app/assets/stylesheets/solid_stack_web/_12_dark_mode.css
|
|
145
147
|
- app/assets/stylesheets/solid_stack_web/application.css
|
|
146
148
|
- app/controllers/solid_stack_web/application_controller.rb
|
|
149
|
+
- app/controllers/solid_stack_web/cable/channel_purges_controller.rb
|
|
150
|
+
- app/controllers/solid_stack_web/cable/purges_controller.rb
|
|
147
151
|
- app/controllers/solid_stack_web/cable_controller.rb
|
|
152
|
+
- app/controllers/solid_stack_web/cable_messages_controller.rb
|
|
148
153
|
- app/controllers/solid_stack_web/cache/flushes_controller.rb
|
|
149
154
|
- app/controllers/solid_stack_web/cache_controller.rb
|
|
150
155
|
- app/controllers/solid_stack_web/cache_entries_controller.rb
|
|
@@ -169,8 +174,11 @@ files:
|
|
|
169
174
|
- app/javascript/solid_stack_web/search_controller.js
|
|
170
175
|
- app/javascript/solid_stack_web/selection_controller.js
|
|
171
176
|
- app/javascript/solid_stack_web/sparkline_tooltip_controller.js
|
|
177
|
+
- app/javascript/solid_stack_web/theme_controller.js
|
|
178
|
+
- app/javascript/solid_stack_web/timestamp_controller.js
|
|
172
179
|
- app/models/solid_stack_web/alert_webhook.rb
|
|
173
180
|
- app/models/solid_stack_web/cable_stats.rb
|
|
181
|
+
- app/models/solid_stack_web/cable_timeline.rb
|
|
174
182
|
- app/models/solid_stack_web/cache_size_stats.rb
|
|
175
183
|
- app/models/solid_stack_web/cache_stats.rb
|
|
176
184
|
- app/models/solid_stack_web/cache_timeline.rb
|
|
@@ -180,6 +188,7 @@ files:
|
|
|
180
188
|
- app/models/solid_stack_web/throughput_sparkline.rb
|
|
181
189
|
- app/views/layouts/solid_stack_web/application.html.erb
|
|
182
190
|
- app/views/solid_stack_web/cable/index.html.erb
|
|
191
|
+
- app/views/solid_stack_web/cable_messages/index.html.erb
|
|
183
192
|
- app/views/solid_stack_web/cache/index.html.erb
|
|
184
193
|
- app/views/solid_stack_web/cache_entries/index.html.erb
|
|
185
194
|
- app/views/solid_stack_web/cache_entries/show.html.erb
|
|
@@ -197,6 +206,7 @@ files:
|
|
|
197
206
|
- app/views/solid_stack_web/queues/show.html.erb
|
|
198
207
|
- app/views/solid_stack_web/recurring_tasks/index.html.erb
|
|
199
208
|
- app/views/solid_stack_web/scheduled_jobs/update.turbo_stream.erb
|
|
209
|
+
- app/views/solid_stack_web/shared/_flash.html.erb
|
|
200
210
|
- app/views/solid_stack_web/stats/index.html.erb
|
|
201
211
|
- config/importmap.rb
|
|
202
212
|
- config/routes.rb
|