pgbus 0.2.5 → 0.2.7
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 +33 -3
- data/app/controllers/pgbus/application_controller.rb +8 -2
- data/app/controllers/pgbus/queues_controller.rb +6 -0
- data/app/helpers/pgbus/application_helper.rb +10 -0
- data/app/views/layouts/pgbus/application.html.erb +212 -25
- data/app/views/pgbus/dashboard/_processes_table.html.erb +5 -5
- data/app/views/pgbus/dashboard/_queues_table.html.erb +6 -6
- data/app/views/pgbus/dashboard/_recent_failures.html.erb +4 -4
- data/app/views/pgbus/dead_letter/_messages_table.html.erb +1 -1
- data/app/views/pgbus/events/index.html.erb +8 -8
- data/app/views/pgbus/insights/show.html.erb +31 -29
- data/app/views/pgbus/jobs/_enqueued_table.html.erb +1 -1
- data/app/views/pgbus/jobs/_failed_table.html.erb +7 -7
- data/app/views/pgbus/locks/index.html.erb +7 -7
- data/app/views/pgbus/outbox/index.html.erb +7 -7
- data/app/views/pgbus/processes/_processes_table.html.erb +7 -7
- data/app/views/pgbus/queues/_queues_list.html.erb +12 -8
- data/app/views/pgbus/queues/show.html.erb +20 -1
- data/app/views/pgbus/recurring_tasks/_tasks_table.html.erb +8 -8
- data/config/locales/da.yml +18 -0
- data/config/locales/de.yml +18 -0
- data/config/locales/en.yml +18 -0
- data/config/locales/es.yml +18 -0
- data/config/locales/fi.yml +18 -0
- data/config/locales/fr.yml +18 -0
- data/config/locales/it.yml +18 -0
- data/config/locales/ja.yml +18 -0
- data/config/locales/nb.yml +18 -0
- data/config/locales/nl.yml +18 -0
- data/config/locales/pt.yml +18 -0
- data/config/locales/sv.yml +18 -0
- data/config/routes.rb +1 -1
- data/lib/pgbus/client.rb +10 -2
- data/lib/pgbus/configuration.rb +3 -1
- data/lib/pgbus/version.rb +1 -1
- data/lib/pgbus/web/data_source.rb +5 -1
- metadata +1 -1
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
<div class="px-5 py-4 border-b border-gray-200 dark:border-gray-700">
|
|
69
69
|
<h3 class="text-sm font-medium text-gray-700 dark:text-gray-300"><%= t("pgbus.insights.show.slowest.title") %></h3>
|
|
70
70
|
</div>
|
|
71
|
-
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
71
|
+
<table class="pgbus-table min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
72
72
|
<thead class="bg-gray-50 dark:bg-gray-900">
|
|
73
73
|
<tr>
|
|
74
74
|
<th class="px-4 py-3 text-left text-xs font-medium uppercase text-gray-500 dark:text-gray-400"><%= t("pgbus.insights.show.slowest.headers.job_class") %></th>
|
|
@@ -80,10 +80,10 @@
|
|
|
80
80
|
<tbody class="divide-y divide-gray-100 dark:divide-gray-700">
|
|
81
81
|
<% @slowest.each do |row| %>
|
|
82
82
|
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700/50">
|
|
83
|
-
<td class="px-4 py-3 text-sm font-medium text-gray-700 dark:text-gray-300"><%= row[:job_class] %></td>
|
|
84
|
-
<td class="px-4 py-3 text-sm text-right font-mono text-gray-700 dark:text-gray-300"><%= pgbus_number(row[:count]) %></td>
|
|
85
|
-
<td class="px-4 py-3 text-sm text-right font-mono text-gray-700 dark:text-gray-300"><%= pgbus_ms_duration(row[:avg_ms]) %></td>
|
|
86
|
-
<td class="px-4 py-3 text-sm text-right font-mono text-gray-700 dark:text-gray-300"><%= pgbus_ms_duration(row[:max_ms]) %></td>
|
|
83
|
+
<td data-label="Job Class" class="px-4 py-3 text-sm font-medium text-gray-700 dark:text-gray-300"><%= row[:job_class] %></td>
|
|
84
|
+
<td data-label="Count" class="px-4 py-3 text-sm text-right font-mono text-gray-700 dark:text-gray-300"><%= pgbus_number(row[:count]) %></td>
|
|
85
|
+
<td data-label="Avg" class="px-4 py-3 text-sm text-right font-mono text-gray-700 dark:text-gray-300"><%= pgbus_ms_duration(row[:avg_ms]) %></td>
|
|
86
|
+
<td data-label="Max" class="px-4 py-3 text-sm text-right font-mono text-gray-700 dark:text-gray-300"><%= pgbus_ms_duration(row[:max_ms]) %></td>
|
|
87
87
|
</tr>
|
|
88
88
|
<% end %>
|
|
89
89
|
<% if @slowest.empty? %>
|
|
@@ -95,12 +95,14 @@
|
|
|
95
95
|
|
|
96
96
|
<script src="https://cdn.jsdelivr.net/npm/apexcharts@4"></script>
|
|
97
97
|
<script>
|
|
98
|
-
|
|
98
|
+
(function() {
|
|
99
|
+
// IIFE prevents "redeclaration of let" when Turbo re-executes on navigation
|
|
100
|
+
var throughputChart, statusChart;
|
|
99
101
|
|
|
100
102
|
function getThemeColors() {
|
|
101
|
-
|
|
103
|
+
var isDark = document.documentElement.classList.contains('dark');
|
|
102
104
|
return {
|
|
103
|
-
isDark,
|
|
105
|
+
isDark: isDark,
|
|
104
106
|
text: isDark ? '#9ca3af' : '#6b7280',
|
|
105
107
|
grid: isDark ? '#374151' : '#e5e7eb',
|
|
106
108
|
tooltip: isDark ? 'dark' : 'light',
|
|
@@ -109,16 +111,14 @@
|
|
|
109
111
|
}
|
|
110
112
|
|
|
111
113
|
function renderCharts(data) {
|
|
112
|
-
|
|
114
|
+
var t = getThemeColors();
|
|
113
115
|
|
|
114
|
-
// Destroy existing charts before re-rendering
|
|
115
116
|
if (throughputChart) throughputChart.destroy();
|
|
116
117
|
if (statusChart) statusChart.destroy();
|
|
117
118
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}));
|
|
119
|
+
var throughputData = data.throughput.map(function(p) {
|
|
120
|
+
return { x: new Date(p.time).getTime(), y: p.count };
|
|
121
|
+
});
|
|
122
122
|
|
|
123
123
|
throughputChart = new ApexCharts(document.querySelector('#throughput-chart'), {
|
|
124
124
|
series: [{ name: '<%= j(t("pgbus.insights.show.charts.series_name")) %>', data: throughputData }],
|
|
@@ -134,10 +134,9 @@
|
|
|
134
134
|
});
|
|
135
135
|
throughputChart.render();
|
|
136
136
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const statusColors = statusLabels.map(s => {
|
|
137
|
+
var statusLabels = Object.keys(data.status_counts);
|
|
138
|
+
var statusValues = Object.values(data.status_counts);
|
|
139
|
+
var statusColors = statusLabels.map(function(s) {
|
|
141
140
|
if (s === 'success') return '#10b981';
|
|
142
141
|
if (s === 'failed') return '#ef4444';
|
|
143
142
|
if (s === 'dead_lettered') return '#f97316';
|
|
@@ -161,21 +160,24 @@
|
|
|
161
160
|
}
|
|
162
161
|
}
|
|
163
162
|
|
|
164
|
-
|
|
165
|
-
let chartData = null;
|
|
163
|
+
var chartData = null;
|
|
166
164
|
fetch('<%= pgbus.api_insights_path(minutes: @minutes) %>')
|
|
167
|
-
.then(r
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
165
|
+
.then(function(r) {
|
|
166
|
+
if (!r.ok) throw new Error('HTTP ' + r.status);
|
|
167
|
+
return r.json();
|
|
168
|
+
})
|
|
169
|
+
.then(function(data) { chartData = data; renderCharts(data); })
|
|
170
|
+
.catch(function(err) {
|
|
171
|
+
if (err.name === 'AbortError') return;
|
|
172
|
+
var msg = '<p class="text-center text-sm text-gray-400 dark:text-gray-500 pt-24"><%= j(t("pgbus.insights.show.charts.failed_to_load")) %></p>';
|
|
173
|
+
var el1 = document.querySelector('#throughput-chart');
|
|
174
|
+
var el2 = document.querySelector('#status-chart');
|
|
175
|
+
if (el1) el1.innerHTML = msg;
|
|
176
|
+
if (el2) el2.innerHTML = msg;
|
|
173
177
|
});
|
|
174
178
|
|
|
175
|
-
// Re-render charts when dark mode toggles.
|
|
176
|
-
// Listen for class changes on <html> instead of wrapping the toggle function,
|
|
177
|
-
// so it works regardless of script loading order.
|
|
178
179
|
new MutationObserver(function() {
|
|
179
180
|
if (chartData) renderCharts(chartData);
|
|
180
181
|
}).observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });
|
|
182
|
+
})();
|
|
181
183
|
</script>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<div>
|
|
3
3
|
<h2 class="text-lg font-semibold text-gray-900 dark:text-white mb-3"><%= t("pgbus.jobs.enqueued_table.title") %></h2>
|
|
4
4
|
<div class="overflow-hidden rounded-lg bg-white dark:bg-gray-800 shadow ring-1 ring-gray-200 dark:ring-gray-700">
|
|
5
|
-
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
5
|
+
<table class="pgbus-table min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
6
6
|
<thead class="bg-gray-50 dark:bg-gray-900">
|
|
7
7
|
<tr>
|
|
8
8
|
<th class="px-4 py-3 text-left text-xs font-medium uppercase text-gray-500"><%= t("pgbus.jobs.enqueued_table.headers.id") %></th>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<div class="mb-8">
|
|
3
3
|
<h2 class="text-lg font-semibold text-gray-900 dark:text-white mb-3"><%= t("pgbus.jobs.failed_table.title") %></h2>
|
|
4
4
|
<div class="overflow-hidden rounded-lg bg-white dark:bg-gray-800 shadow ring-1 ring-gray-200 dark:ring-gray-700">
|
|
5
|
-
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
5
|
+
<table class="pgbus-table min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
6
6
|
<thead class="bg-gray-50 dark:bg-gray-900">
|
|
7
7
|
<tr>
|
|
8
8
|
<th class="px-4 py-3 text-left text-xs font-medium uppercase text-gray-500"><%= t("pgbus.jobs.failed_table.headers.id") %></th>
|
|
@@ -16,16 +16,16 @@
|
|
|
16
16
|
<tbody class="divide-y divide-gray-100 dark:divide-gray-700">
|
|
17
17
|
<% @failed.each do |f| %>
|
|
18
18
|
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700/50 dark:bg-gray-900">
|
|
19
|
-
<td class="px-4 py-3 text-sm font-mono text-gray-900 dark:text-white">
|
|
19
|
+
<td data-label="ID" class="px-4 py-3 text-sm font-mono text-gray-900 dark:text-white">
|
|
20
20
|
<%= link_to f["id"], pgbus.job_path(f["id"]), class: "text-indigo-600 hover:text-indigo-500", data: { turbo_frame: "_top" } %>
|
|
21
21
|
</td>
|
|
22
|
-
<td class="px-4 py-3 text-sm text-gray-700"><%= f["queue_name"] %></td>
|
|
23
|
-
<td class="px-4 py-3 text-sm text-red-600 max-w-sm truncate">
|
|
22
|
+
<td data-label="Queue" class="px-4 py-3 text-sm text-gray-700"><%= f["queue_name"] %></td>
|
|
23
|
+
<td data-label="Error" class="px-4 py-3 text-sm text-red-600 max-w-sm truncate">
|
|
24
24
|
<span class="font-medium"><%= f["error_class"] %></span>: <%= truncate(f["error_message"].to_s, length: 80) %>
|
|
25
25
|
</td>
|
|
26
|
-
<td class="px-4 py-3 text-sm text-gray-500"><%= f["retry_count"] %></td>
|
|
27
|
-
<td class="px-4 py-3 text-sm text-gray-500"><%= pgbus_time_ago(f["failed_at"]) %></td>
|
|
28
|
-
<td class="px-4 py-3 text-sm text-right space-x-2">
|
|
26
|
+
<td data-label="Retries" class="px-4 py-3 text-sm text-gray-500"><%= f["retry_count"] %></td>
|
|
27
|
+
<td data-label="Failed" class="px-4 py-3 text-sm text-gray-500"><%= pgbus_time_ago(f["failed_at"]) %></td>
|
|
28
|
+
<td data-label="Actions" class="px-4 py-3 text-sm text-right space-x-2">
|
|
29
29
|
<%= button_to t("pgbus.jobs.failed_table.retry"), pgbus.retry_job_path(f["id"]), method: :post,
|
|
30
30
|
class: "text-xs text-indigo-600 hover:text-indigo-800 font-medium",
|
|
31
31
|
data: { turbo_frame: "_top" } %>
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
</div>
|
|
5
5
|
|
|
6
6
|
<div class="overflow-hidden rounded-lg bg-white dark:bg-gray-800 shadow ring-1 ring-gray-200 dark:ring-gray-700">
|
|
7
|
-
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
7
|
+
<table class="pgbus-table min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
8
8
|
<thead class="bg-gray-50 dark:bg-gray-900">
|
|
9
9
|
<tr>
|
|
10
10
|
<th class="px-4 py-3 text-left text-xs font-medium uppercase text-gray-500 dark:text-gray-400"><%= t("pgbus.locks.index.headers.lock_key") %></th>
|
|
@@ -18,16 +18,16 @@
|
|
|
18
18
|
<tbody class="divide-y divide-gray-100 dark:divide-gray-700">
|
|
19
19
|
<% @locks.each do |lock| %>
|
|
20
20
|
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700/50">
|
|
21
|
-
<td class="px-4 py-3 text-sm font-mono text-gray-700 dark:text-gray-300 max-w-xs truncate"><%= lock[:lock_key] %></td>
|
|
22
|
-
<td class="px-4 py-3 text-sm text-gray-700 dark:text-gray-300"><%= lock[:job_class] %></td>
|
|
23
|
-
<td class="px-4 py-3 text-sm">
|
|
21
|
+
<td data-label="Lock Key" class="px-4 py-3 text-sm font-mono text-gray-700 dark:text-gray-300 max-w-xs truncate"><%= lock[:lock_key] %></td>
|
|
22
|
+
<td data-label="Job Class" class="px-4 py-3 text-sm text-gray-700 dark:text-gray-300"><%= lock[:job_class] %></td>
|
|
23
|
+
<td data-label="State" class="px-4 py-3 text-sm">
|
|
24
24
|
<% if lock[:state] == "executing" %>
|
|
25
25
|
<span class="inline-flex items-center rounded-full bg-blue-100 dark:bg-blue-900/50 px-2.5 py-0.5 text-xs font-medium text-blue-800 dark:text-blue-300"><%= t("pgbus.locks.index.executing") %></span>
|
|
26
26
|
<% else %>
|
|
27
27
|
<span class="inline-flex items-center rounded-full bg-yellow-100 dark:bg-yellow-900/50 px-2.5 py-0.5 text-xs font-medium text-yellow-800 dark:text-yellow-300"><%= t("pgbus.locks.index.queued") %></span>
|
|
28
28
|
<% end %>
|
|
29
29
|
</td>
|
|
30
|
-
<td class="px-4 py-3 text-sm text-gray-500 dark:text-gray-400">
|
|
30
|
+
<td data-label="Owner" class="px-4 py-3 text-sm text-gray-500 dark:text-gray-400">
|
|
31
31
|
<% if lock[:owner_pid] %>
|
|
32
32
|
<span class="font-mono"><%= lock[:owner_pid] %></span>
|
|
33
33
|
<% if lock[:owner_hostname] %>
|
|
@@ -37,12 +37,12 @@
|
|
|
37
37
|
<span class="text-gray-400 dark:text-gray-500">—</span>
|
|
38
38
|
<% end %>
|
|
39
39
|
</td>
|
|
40
|
-
<td class="px-4 py-3 text-sm text-right text-gray-500 dark:text-gray-400">
|
|
40
|
+
<td data-label="Age" class="px-4 py-3 text-sm text-right text-gray-500 dark:text-gray-400">
|
|
41
41
|
<% if lock[:age_seconds] %>
|
|
42
42
|
<%= pgbus_duration(lock[:age_seconds]) %>
|
|
43
43
|
<% end %>
|
|
44
44
|
</td>
|
|
45
|
-
<td class="px-4 py-3 text-sm text-right text-gray-500 dark:text-gray-400"><%= pgbus_time_ago_future(lock[:expires_at]) %></td>
|
|
45
|
+
<td data-label="Expires" class="px-4 py-3 text-sm text-right text-gray-500 dark:text-gray-400"><%= pgbus_time_ago_future(lock[:expires_at]) %></td>
|
|
46
46
|
</tr>
|
|
47
47
|
<% end %>
|
|
48
48
|
<% if @locks.empty? %>
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
</div>
|
|
20
20
|
|
|
21
21
|
<div class="overflow-hidden rounded-lg bg-white dark:bg-gray-800 shadow ring-1 ring-gray-200 dark:ring-gray-700">
|
|
22
|
-
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
22
|
+
<table class="pgbus-table min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
23
23
|
<thead class="bg-gray-50 dark:bg-gray-900">
|
|
24
24
|
<tr>
|
|
25
25
|
<th class="px-4 py-3 text-left text-xs font-medium uppercase text-gray-500"><%= t("pgbus.outbox.index.headers.id") %></th>
|
|
@@ -33,18 +33,18 @@
|
|
|
33
33
|
<tbody class="divide-y divide-gray-100 dark:divide-gray-700">
|
|
34
34
|
<% @entries.each do |entry| %>
|
|
35
35
|
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700/50 dark:bg-gray-900">
|
|
36
|
-
<td class="px-4 py-3 text-sm font-mono text-gray-700"><%= entry.id %></td>
|
|
37
|
-
<td class="px-4 py-3 text-sm text-gray-700"><%= entry.routing_key || entry.queue_name %></td>
|
|
38
|
-
<td class="px-4 py-3 text-sm text-gray-500 max-w-xs truncate"><%= pgbus_json_preview(entry.payload) %></td>
|
|
39
|
-
<td class="px-4 py-3 text-sm text-right text-gray-500"><%= entry.priority %></td>
|
|
40
|
-
<td class="px-4 py-3 text-sm text-right">
|
|
36
|
+
<td data-label="ID" class="px-4 py-3 text-sm font-mono text-gray-700"><%= entry.id %></td>
|
|
37
|
+
<td data-label="Routing Key" class="px-4 py-3 text-sm text-gray-700"><%= entry.routing_key || entry.queue_name %></td>
|
|
38
|
+
<td data-label="Payload" class="px-4 py-3 text-sm text-gray-500 max-w-xs truncate"><%= pgbus_json_preview(entry.payload) %></td>
|
|
39
|
+
<td data-label="Priority" class="px-4 py-3 text-sm text-right text-gray-500"><%= entry.priority %></td>
|
|
40
|
+
<td data-label="Status" class="px-4 py-3 text-sm text-right">
|
|
41
41
|
<% if entry.published_at %>
|
|
42
42
|
<span class="inline-flex items-center rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-800"><%= t("pgbus.outbox.index.published") %></span>
|
|
43
43
|
<% else %>
|
|
44
44
|
<span class="inline-flex items-center rounded-full bg-yellow-100 px-2 py-0.5 text-xs font-medium text-yellow-800"><%= t("pgbus.outbox.index.pending") %></span>
|
|
45
45
|
<% end %>
|
|
46
46
|
</td>
|
|
47
|
-
<td class="px-4 py-3 text-sm text-right text-gray-500"><%= pgbus_time_ago(entry.created_at) %></td>
|
|
47
|
+
<td data-label="Created" class="px-4 py-3 text-sm text-right text-gray-500"><%= pgbus_time_ago(entry.created_at) %></td>
|
|
48
48
|
</tr>
|
|
49
49
|
<% end %>
|
|
50
50
|
<% if @entries.empty? %>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<turbo-frame id="processes-list" data-auto-refresh data-src="<%= pgbus.processes_path(frame: 'list') %>">
|
|
2
2
|
<div class="overflow-hidden rounded-lg bg-white dark:bg-gray-800 shadow ring-1 ring-gray-200 dark:ring-gray-700">
|
|
3
|
-
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
3
|
+
<table class="pgbus-table min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
4
4
|
<thead class="bg-gray-50 dark:bg-gray-900">
|
|
5
5
|
<tr>
|
|
6
6
|
<th class="px-4 py-3 text-left text-xs font-medium uppercase text-gray-500"><%= t("pgbus.processes.processes_table.headers.kind") %></th>
|
|
@@ -14,12 +14,12 @@
|
|
|
14
14
|
<tbody class="divide-y divide-gray-100 dark:divide-gray-700">
|
|
15
15
|
<% @processes.each do |p| %>
|
|
16
16
|
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700/50 dark:bg-gray-900">
|
|
17
|
-
<td class="px-4 py-3 text-sm font-medium text-gray-900 dark:text-white"><%= p[:kind] %></td>
|
|
18
|
-
<td class="px-4 py-3 text-sm text-gray-700"><%= p[:hostname] %></td>
|
|
19
|
-
<td class="px-4 py-3 text-sm font-mono text-gray-700"><%= p[:pid] %></td>
|
|
20
|
-
<td class="px-4 py-3 text-sm"><%= pgbus_status_badge(p[:healthy]) %></td>
|
|
21
|
-
<td class="px-4 py-3 text-sm text-gray-500"><%= pgbus_time_ago(p[:last_heartbeat_at]) %></td>
|
|
22
|
-
<td class="px-4 py-3 text-sm text-gray-500 font-mono text-xs max-w-xs truncate">
|
|
17
|
+
<td data-label="Kind" class="px-4 py-3 text-sm font-medium text-gray-900 dark:text-white"><%= p[:kind] %></td>
|
|
18
|
+
<td data-label="Host" class="px-4 py-3 text-sm text-gray-700"><%= p[:hostname] %></td>
|
|
19
|
+
<td data-label="PID" class="px-4 py-3 text-sm font-mono text-gray-700"><%= p[:pid] %></td>
|
|
20
|
+
<td data-label="Status" class="px-4 py-3 text-sm"><%= pgbus_status_badge(p[:healthy]) %></td>
|
|
21
|
+
<td data-label="Last Heartbeat" class="px-4 py-3 text-sm text-gray-500"><%= pgbus_time_ago(p[:last_heartbeat_at]) %></td>
|
|
22
|
+
<td data-label="Metadata" class="px-4 py-3 text-sm text-gray-500 font-mono text-xs max-w-xs truncate">
|
|
23
23
|
<% if p[:metadata].is_a?(Hash) %>
|
|
24
24
|
<% p[:metadata].each do |k, v| %>
|
|
25
25
|
<span class="inline-flex items-center rounded bg-gray-100 px-1.5 py-0.5 text-xs mr-1"><%= k %>: <%= v %></span>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<turbo-frame id="queues-list" data-auto-refresh data-src="<%= pgbus.queues_path(frame: 'list') %>">
|
|
2
2
|
<div class="overflow-hidden rounded-lg bg-white dark:bg-gray-800 shadow ring-1 ring-gray-200 dark:ring-gray-700">
|
|
3
|
-
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
3
|
+
<table class="pgbus-table min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
4
4
|
<thead class="bg-gray-50 dark:bg-gray-900">
|
|
5
5
|
<tr>
|
|
6
6
|
<th class="px-4 py-3 text-left text-xs font-medium uppercase text-gray-500"><%= t("pgbus.queues.queues_list.headers.queue") %></th>
|
|
@@ -15,19 +15,19 @@
|
|
|
15
15
|
<tbody class="divide-y divide-gray-100 dark:divide-gray-700">
|
|
16
16
|
<% @queues.each do |q| %>
|
|
17
17
|
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700/50 dark:bg-gray-900">
|
|
18
|
-
<td class="px-4 py-3 text-sm">
|
|
18
|
+
<td data-label="Queue" class="px-4 py-3 text-sm">
|
|
19
19
|
<%= link_to q[:name], pgbus.queue_path(name: q[:name]), class: "font-medium text-indigo-600 hover:text-indigo-500", data: { turbo_frame: "_top" } %>
|
|
20
20
|
<%= pgbus_queue_badge(q[:name]) %>
|
|
21
21
|
<% if q[:paused] %>
|
|
22
22
|
<span class="inline-flex items-center rounded-full bg-yellow-100 px-2 py-0.5 text-xs font-medium text-yellow-800"><%= t("pgbus.queues.queues_list.paused") %></span>
|
|
23
23
|
<% end %>
|
|
24
24
|
</td>
|
|
25
|
-
<td class="px-4 py-3 text-sm text-right font-mono text-gray-700"><%= pgbus_number(q[:queue_length]) %></td>
|
|
26
|
-
<td class="px-4 py-3 text-sm text-right font-mono text-gray-700"><%= pgbus_number(q[:queue_visible_length]) %></td>
|
|
27
|
-
<td class="px-4 py-3 text-sm text-right text-gray-500"><%= q[:oldest_msg_age_sec] || "—" %></td>
|
|
28
|
-
<td class="px-4 py-3 text-sm text-right text-gray-500"><%= q[:newest_msg_age_sec] || "—" %></td>
|
|
29
|
-
<td class="px-4 py-3 text-sm text-right text-gray-500"><%= pgbus_number(q[:total_messages]) %></td>
|
|
30
|
-
<td class="px-4 py-3 text-sm text-right space-x-2">
|
|
25
|
+
<td data-label="Depth" class="px-4 py-3 text-sm text-right font-mono text-gray-700"><%= pgbus_number(q[:queue_length]) %></td>
|
|
26
|
+
<td data-label="Visible" class="px-4 py-3 text-sm text-right font-mono text-gray-700"><%= pgbus_number(q[:queue_visible_length]) %></td>
|
|
27
|
+
<td data-label="Oldest" class="px-4 py-3 text-sm text-right text-gray-500"><%= q[:oldest_msg_age_sec] || "—" %></td>
|
|
28
|
+
<td data-label="Newest" class="px-4 py-3 text-sm text-right text-gray-500"><%= q[:newest_msg_age_sec] || "—" %></td>
|
|
29
|
+
<td data-label="Total" class="px-4 py-3 text-sm text-right text-gray-500"><%= pgbus_number(q[:total_messages]) %></td>
|
|
30
|
+
<td data-label="Actions" class="px-4 py-3 text-sm text-right space-x-2">
|
|
31
31
|
<% if q[:paused] %>
|
|
32
32
|
<%= button_to t("pgbus.queues.queues_list.resume"), pgbus.resume_queue_path(name: q[:name]),
|
|
33
33
|
method: :post,
|
|
@@ -43,6 +43,10 @@
|
|
|
43
43
|
method: :post,
|
|
44
44
|
class: "text-xs text-red-600 hover:text-red-800 font-medium",
|
|
45
45
|
data: { turbo_confirm: t("pgbus.queues.queues_list.purge_confirm", name: q[:name]), turbo_frame: "_top" } %>
|
|
46
|
+
<%= button_to t("pgbus.queues.queues_list.delete"), pgbus.queue_path(name: q[:name]),
|
|
47
|
+
method: :delete,
|
|
48
|
+
class: "text-xs text-red-600 hover:text-red-800 font-medium",
|
|
49
|
+
data: { turbo_confirm: t("pgbus.queues.queues_list.delete_confirm", name: q[:name]), turbo_frame: "_top" } %>
|
|
46
50
|
</td>
|
|
47
51
|
</tr>
|
|
48
52
|
<% end %>
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
<div class="flex items-center justify-between mb-6">
|
|
2
2
|
<div>
|
|
3
|
-
<h1 class="text-2xl font-bold text-gray-900 dark:text-white"
|
|
3
|
+
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">
|
|
4
|
+
<%= @queue&.dig(:name) || params[:name] %>
|
|
5
|
+
<% if @paused %>
|
|
6
|
+
<span class="inline-flex items-center rounded-full bg-yellow-100 px-2.5 py-0.5 text-xs font-medium text-yellow-800"><%= t("pgbus.queues.queues_list.paused") %></span>
|
|
7
|
+
<% end %>
|
|
8
|
+
</h1>
|
|
4
9
|
<% if @queue %>
|
|
5
10
|
<p class="text-sm text-gray-500 mt-1">
|
|
6
11
|
<%= t("pgbus.queues.show.depth") %> <span class="font-mono"><%= @queue[:queue_length] %></span> |
|
|
@@ -10,10 +15,24 @@
|
|
|
10
15
|
<% end %>
|
|
11
16
|
</div>
|
|
12
17
|
<div class="flex space-x-2">
|
|
18
|
+
<% if @paused %>
|
|
19
|
+
<%= button_to t("pgbus.queues.show.resume"), pgbus.resume_queue_path(name: params[:name]),
|
|
20
|
+
method: :post,
|
|
21
|
+
class: "rounded-md bg-green-600 px-3 py-2 text-sm font-medium text-white hover:bg-green-500" %>
|
|
22
|
+
<% else %>
|
|
23
|
+
<%= button_to t("pgbus.queues.show.pause"), pgbus.pause_queue_path(name: params[:name]),
|
|
24
|
+
method: :post,
|
|
25
|
+
class: "rounded-md bg-yellow-500 px-3 py-2 text-sm font-medium text-white hover:bg-yellow-400",
|
|
26
|
+
data: { turbo_confirm: t("pgbus.queues.show.pause_confirm") } %>
|
|
27
|
+
<% end %>
|
|
13
28
|
<%= button_to t("pgbus.queues.show.purge_queue"), pgbus.purge_queue_path(name: params[:name]),
|
|
14
29
|
method: :post,
|
|
15
30
|
class: "rounded-md bg-red-600 px-3 py-2 text-sm font-medium text-white hover:bg-red-500",
|
|
16
31
|
data: { turbo_confirm: t("pgbus.queues.show.purge_confirm") } %>
|
|
32
|
+
<%= button_to t("pgbus.queues.show.delete_queue"), pgbus.queue_path(name: params[:name]),
|
|
33
|
+
method: :delete,
|
|
34
|
+
class: "rounded-md bg-red-800 px-3 py-2 text-sm font-medium text-white hover:bg-red-700",
|
|
35
|
+
data: { turbo_confirm: t("pgbus.queues.show.delete_confirm") } %>
|
|
17
36
|
</div>
|
|
18
37
|
</div>
|
|
19
38
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<p class="mt-1 text-sm"><%= t("pgbus.recurring_tasks.tasks_table.empty_hint") %></p>
|
|
7
7
|
</div>
|
|
8
8
|
<% else %>
|
|
9
|
-
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
9
|
+
<table class="pgbus-table min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
10
10
|
<thead class="bg-gray-50 dark:bg-gray-900">
|
|
11
11
|
<tr>
|
|
12
12
|
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase"><%= t("pgbus.recurring_tasks.tasks_table.headers.task") %></th>
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
<tbody class="divide-y divide-gray-200 dark:divide-gray-700">
|
|
22
22
|
<% @recurring_tasks.each do |task| %>
|
|
23
23
|
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700/50 dark:bg-gray-900">
|
|
24
|
-
<td class="px-4 py-3">
|
|
24
|
+
<td data-label="Task" class="px-4 py-3">
|
|
25
25
|
<div>
|
|
26
26
|
<%= link_to task[:key], pgbus.recurring_task_path(task[:id]),
|
|
27
27
|
class: "text-sm font-medium text-blue-600 hover:text-blue-800" %>
|
|
@@ -31,26 +31,26 @@
|
|
|
31
31
|
<div class="text-xs text-gray-400 mt-0.5"><%= task[:description] %></div>
|
|
32
32
|
<% end %>
|
|
33
33
|
</td>
|
|
34
|
-
<td class="px-4 py-3">
|
|
34
|
+
<td data-label="Schedule" class="px-4 py-3">
|
|
35
35
|
<div class="text-sm text-gray-900 dark:text-white"><%= task[:schedule] %></div>
|
|
36
36
|
<% if task[:human_schedule] && task[:human_schedule] != task[:schedule] %>
|
|
37
37
|
<div class="text-xs text-gray-400"><%= task[:human_schedule] %></div>
|
|
38
38
|
<% end %>
|
|
39
39
|
</td>
|
|
40
|
-
<td class="px-4 py-3 text-sm text-gray-600">
|
|
40
|
+
<td data-label="Queue" class="px-4 py-3 text-sm text-gray-600">
|
|
41
41
|
<%= task[:queue_name] || t("pgbus.recurring_tasks.tasks_table.default_queue") %>
|
|
42
42
|
</td>
|
|
43
|
-
<td class="px-4 py-3 text-sm text-gray-600">
|
|
43
|
+
<td data-label="Last Run" class="px-4 py-3 text-sm text-gray-600">
|
|
44
44
|
<%= task[:last_run_at] ? pgbus_time_ago(task[:last_run_at]) : t("pgbus.recurring_tasks.tasks_table.never") %>
|
|
45
45
|
</td>
|
|
46
|
-
<td class="px-4 py-3 text-sm text-gray-600">
|
|
46
|
+
<td data-label="Next Run" class="px-4 py-3 text-sm text-gray-600">
|
|
47
47
|
<% if task[:enabled] && task[:next_run_at] %>
|
|
48
48
|
<%= pgbus_time_ago_future(task[:next_run_at]) %>
|
|
49
49
|
<% else %>
|
|
50
50
|
<span class="text-gray-400">—</span>
|
|
51
51
|
<% end %>
|
|
52
52
|
</td>
|
|
53
|
-
<td class="px-4 py-3">
|
|
53
|
+
<td data-label="Status" class="px-4 py-3">
|
|
54
54
|
<% if task[:enabled] %>
|
|
55
55
|
<%= pgbus_recurring_health_badge(task) %>
|
|
56
56
|
<% else %>
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
</span>
|
|
60
60
|
<% end %>
|
|
61
61
|
</td>
|
|
62
|
-
<td class="px-4 py-3 text-right space-x-1">
|
|
62
|
+
<td data-label="Actions" class="px-4 py-3 text-right space-x-1">
|
|
63
63
|
<%= button_to task[:enabled] ? t("pgbus.recurring_tasks.tasks_table.disable") : t("pgbus.recurring_tasks.tasks_table.enable"),
|
|
64
64
|
pgbus.toggle_recurring_task_path(task[:id]),
|
|
65
65
|
class: "inline-flex items-center rounded px-2 py-1 text-xs font-medium " \
|
data/config/locales/da.yml
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
da:
|
|
3
3
|
pgbus:
|
|
4
|
+
dialogs:
|
|
5
|
+
cancel: Annuller
|
|
6
|
+
confirm: Bekræft
|
|
7
|
+
confirm_title: Er du sikker?
|
|
8
|
+
delete: Slet
|
|
9
|
+
delete_title: Slet
|
|
10
|
+
ok: OK
|
|
4
11
|
dashboard:
|
|
5
12
|
processes_table:
|
|
6
13
|
empty: Ingen processer kører
|
|
@@ -217,8 +224,10 @@ da:
|
|
|
217
224
|
processes: Processer
|
|
218
225
|
queues: Køer
|
|
219
226
|
recurring: Gentagende
|
|
227
|
+
return_to_app: Tilbage til app
|
|
220
228
|
title: Pgbus Dashboard
|
|
221
229
|
toggle_dark_mode: Skift til mørk tilstand
|
|
230
|
+
toggle_menu: Skift menu
|
|
222
231
|
locks:
|
|
223
232
|
index:
|
|
224
233
|
description: Aktive unikke låse forhindrer duplikeret jobudførelse
|
|
@@ -278,13 +287,19 @@ da:
|
|
|
278
287
|
queue: Kø
|
|
279
288
|
total_ever: Total nogensinde
|
|
280
289
|
visible: Synlig
|
|
290
|
+
delete: Slet
|
|
291
|
+
delete_confirm: Slet køen %{name} permanent? Dette kan ikke fortrydes.
|
|
281
292
|
pause: Pause
|
|
282
293
|
pause_confirm: Pause behandling i %{name}?
|
|
283
294
|
paused: Pauset
|
|
284
295
|
purge: Rens
|
|
285
296
|
purge_confirm: Rens alle beskeder fra %{name}?
|
|
286
297
|
resume: Genoptag
|
|
298
|
+
destroy:
|
|
299
|
+
success: Køen %{name} slettet.
|
|
287
300
|
show:
|
|
301
|
+
delete_confirm: Slet denne kø permanent? Dette kan ikke fortrydes.
|
|
302
|
+
delete_queue: Slet kø
|
|
288
303
|
depth: 'Dybde:'
|
|
289
304
|
empty: Køen er tom
|
|
290
305
|
headers:
|
|
@@ -293,8 +308,11 @@ da:
|
|
|
293
308
|
payload: Indhold
|
|
294
309
|
reads: Læsninger
|
|
295
310
|
vt: VT
|
|
311
|
+
pause: Pause
|
|
312
|
+
pause_confirm: Pause behandling?
|
|
296
313
|
purge_confirm: Rens alle beskeder?
|
|
297
314
|
purge_queue: Rens kø
|
|
315
|
+
resume: Genoptag
|
|
298
316
|
total: 'Total:'
|
|
299
317
|
visible: 'Synlig:'
|
|
300
318
|
recurring_tasks:
|
data/config/locales/de.yml
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
de:
|
|
3
3
|
pgbus:
|
|
4
|
+
dialogs:
|
|
5
|
+
cancel: Abbrechen
|
|
6
|
+
confirm: Bestätigen
|
|
7
|
+
confirm_title: Sind Sie sicher?
|
|
8
|
+
delete: Löschen
|
|
9
|
+
delete_title: Löschen
|
|
10
|
+
ok: OK
|
|
4
11
|
dashboard:
|
|
5
12
|
processes_table:
|
|
6
13
|
empty: Keine Prozesse laufen
|
|
@@ -217,8 +224,10 @@ de:
|
|
|
217
224
|
processes: Prozesse
|
|
218
225
|
queues: Warteschlangen
|
|
219
226
|
recurring: Wiederkehrend
|
|
227
|
+
return_to_app: Zurück zur App
|
|
220
228
|
title: Pgbus Dashboard
|
|
221
229
|
toggle_dark_mode: Dunkelmodus umschalten
|
|
230
|
+
toggle_menu: Menü umschalten
|
|
222
231
|
locks:
|
|
223
232
|
index:
|
|
224
233
|
description: Aktive Einzigartigkeitssperren verhindern doppelte Auftragserstellung
|
|
@@ -278,13 +287,19 @@ de:
|
|
|
278
287
|
queue: Warteschlange
|
|
279
288
|
total_ever: Insgesamt jemals
|
|
280
289
|
visible: Sichtbar
|
|
290
|
+
delete: Löschen
|
|
291
|
+
delete_confirm: Warteschlange %{name} dauerhaft löschen? Dies kann nicht rückgängig gemacht werden.
|
|
281
292
|
pause: Pause
|
|
282
293
|
pause_confirm: Verarbeitung für %{name} pausieren?
|
|
283
294
|
paused: Pausiert
|
|
284
295
|
purge: Bereinigen
|
|
285
296
|
purge_confirm: Alle Nachrichten von %{name} löschen?
|
|
286
297
|
resume: Fortsetzen
|
|
298
|
+
destroy:
|
|
299
|
+
success: Warteschlange %{name} gelöscht.
|
|
287
300
|
show:
|
|
301
|
+
delete_confirm: Diese Warteschlange dauerhaft löschen? Dies kann nicht rückgängig gemacht werden.
|
|
302
|
+
delete_queue: Warteschlange löschen
|
|
288
303
|
depth: 'Tiefe:'
|
|
289
304
|
empty: Warteschlange ist leer
|
|
290
305
|
headers:
|
|
@@ -293,8 +308,11 @@ de:
|
|
|
293
308
|
payload: Nutzlast
|
|
294
309
|
reads: Lesevorgänge
|
|
295
310
|
vt: VT
|
|
311
|
+
pause: Pause
|
|
312
|
+
pause_confirm: Verarbeitung pausieren?
|
|
296
313
|
purge_confirm: Alle Nachrichten löschen?
|
|
297
314
|
purge_queue: Warteschlange bereinigen
|
|
315
|
+
resume: Fortsetzen
|
|
298
316
|
total: 'Insgesamt:'
|
|
299
317
|
visible: 'Sichtbar:'
|
|
300
318
|
recurring_tasks:
|
data/config/locales/en.yml
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
en:
|
|
3
3
|
pgbus:
|
|
4
|
+
dialogs:
|
|
5
|
+
cancel: Cancel
|
|
6
|
+
confirm: Confirm
|
|
7
|
+
confirm_title: Are you sure?
|
|
8
|
+
delete: Delete
|
|
9
|
+
delete_title: Delete
|
|
10
|
+
ok: OK
|
|
4
11
|
dashboard:
|
|
5
12
|
processes_table:
|
|
6
13
|
empty: No processes running
|
|
@@ -217,8 +224,10 @@ en:
|
|
|
217
224
|
processes: Processes
|
|
218
225
|
queues: Queues
|
|
219
226
|
recurring: Recurring
|
|
227
|
+
return_to_app: Back to app
|
|
220
228
|
title: Pgbus Dashboard
|
|
221
229
|
toggle_dark_mode: Toggle dark mode
|
|
230
|
+
toggle_menu: Toggle menu
|
|
222
231
|
locks:
|
|
223
232
|
index:
|
|
224
233
|
description: Active uniqueness locks preventing duplicate job execution
|
|
@@ -278,13 +287,19 @@ en:
|
|
|
278
287
|
queue: Queue
|
|
279
288
|
total_ever: Total Ever
|
|
280
289
|
visible: Visible
|
|
290
|
+
delete: Delete
|
|
291
|
+
delete_confirm: Permanently delete queue %{name}? This cannot be undone.
|
|
281
292
|
pause: Pause
|
|
282
293
|
pause_confirm: Pause processing for %{name}?
|
|
283
294
|
paused: Paused
|
|
284
295
|
purge: Purge
|
|
285
296
|
purge_confirm: Purge all messages from %{name}?
|
|
286
297
|
resume: Resume
|
|
298
|
+
destroy:
|
|
299
|
+
success: Queue %{name} deleted.
|
|
287
300
|
show:
|
|
301
|
+
delete_confirm: Permanently delete this queue? This cannot be undone.
|
|
302
|
+
delete_queue: Delete Queue
|
|
288
303
|
depth: 'Depth:'
|
|
289
304
|
empty: Queue is empty
|
|
290
305
|
headers:
|
|
@@ -293,8 +308,11 @@ en:
|
|
|
293
308
|
payload: Payload
|
|
294
309
|
reads: Reads
|
|
295
310
|
vt: VT
|
|
311
|
+
pause: Pause
|
|
312
|
+
pause_confirm: Pause processing?
|
|
296
313
|
purge_confirm: Purge all messages?
|
|
297
314
|
purge_queue: Purge Queue
|
|
315
|
+
resume: Resume
|
|
298
316
|
total: 'Total:'
|
|
299
317
|
visible: 'Visible:'
|
|
300
318
|
recurring_tasks:
|
data/config/locales/es.yml
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
es:
|
|
3
3
|
pgbus:
|
|
4
|
+
dialogs:
|
|
5
|
+
cancel: Cancelar
|
|
6
|
+
confirm: Confirmar
|
|
7
|
+
confirm_title: "¿Estás seguro?"
|
|
8
|
+
delete: Eliminar
|
|
9
|
+
delete_title: Eliminar
|
|
10
|
+
ok: Aceptar
|
|
4
11
|
dashboard:
|
|
5
12
|
processes_table:
|
|
6
13
|
empty: No hay procesos en ejecución
|
|
@@ -217,8 +224,10 @@ es:
|
|
|
217
224
|
processes: Procesos
|
|
218
225
|
queues: Colas
|
|
219
226
|
recurring: Recurrente
|
|
227
|
+
return_to_app: Volver a la app
|
|
220
228
|
title: Panel de Pgbus
|
|
221
229
|
toggle_dark_mode: Alternar modo oscuro
|
|
230
|
+
toggle_menu: Alternar menú
|
|
222
231
|
locks:
|
|
223
232
|
index:
|
|
224
233
|
description: Bloqueos de unicidad activos que impiden la ejecución duplicada del trabajo
|
|
@@ -278,13 +287,19 @@ es:
|
|
|
278
287
|
queue: Cola
|
|
279
288
|
total_ever: Total acumulado
|
|
280
289
|
visible: Visible
|
|
290
|
+
delete: Eliminar
|
|
291
|
+
delete_confirm: "¿Eliminar permanentemente la cola %{name}? Esto no se puede deshacer."
|
|
281
292
|
pause: Pausar
|
|
282
293
|
pause_confirm: "¿Pausar el procesamiento de %{name}?"
|
|
283
294
|
paused: Pausado
|
|
284
295
|
purge: Purgar
|
|
285
296
|
purge_confirm: "¿Purgar todos los mensajes de %{name}?"
|
|
286
297
|
resume: Reanudar
|
|
298
|
+
destroy:
|
|
299
|
+
success: Cola %{name} eliminada.
|
|
287
300
|
show:
|
|
301
|
+
delete_confirm: "¿Eliminar permanentemente esta cola? Esto no se puede deshacer."
|
|
302
|
+
delete_queue: Eliminar cola
|
|
288
303
|
depth: 'Profundidad:'
|
|
289
304
|
empty: La cola está vacía
|
|
290
305
|
headers:
|
|
@@ -293,8 +308,11 @@ es:
|
|
|
293
308
|
payload: Carga útil
|
|
294
309
|
reads: Lecturas
|
|
295
310
|
vt: VT
|
|
311
|
+
pause: Pausar
|
|
312
|
+
pause_confirm: "¿Pausar el procesamiento?"
|
|
296
313
|
purge_confirm: "¿Purgar todos los mensajes?"
|
|
297
314
|
purge_queue: Purgar cola
|
|
315
|
+
resume: Reanudar
|
|
298
316
|
total: 'Total:'
|
|
299
317
|
visible: 'Visible:'
|
|
300
318
|
recurring_tasks:
|