solid_stack_web 1.5.0 → 1.6.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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +157 -3
  3. data/app/assets/stylesheets/solid_stack_web/_02_layout.css +12 -0
  4. data/app/assets/stylesheets/solid_stack_web/_07_dashboard.css +4 -3
  5. data/app/controllers/solid_stack_web/application_controller.rb +10 -0
  6. data/app/controllers/solid_stack_web/audit_controller.rb +1 -1
  7. data/app/controllers/solid_stack_web/cable/channel_purges_controller.rb +1 -1
  8. data/app/controllers/solid_stack_web/cable/purges_controller.rb +1 -1
  9. data/app/controllers/solid_stack_web/cache/flushes_controller.rb +1 -1
  10. data/app/controllers/solid_stack_web/cache_entries_controller.rb +1 -1
  11. data/app/controllers/solid_stack_web/failed_jobs/arguments_controller.rb +3 -3
  12. data/app/controllers/solid_stack_web/failed_jobs/selections_controller.rb +4 -4
  13. data/app/controllers/solid_stack_web/failed_jobs_controller.rb +2 -2
  14. data/app/controllers/solid_stack_web/jobs/selections_controller.rb +2 -2
  15. data/app/controllers/solid_stack_web/jobs_controller.rb +2 -2
  16. data/app/controllers/solid_stack_web/recurring_tasks/runs_controller.rb +4 -4
  17. data/app/controllers/solid_stack_web/scheduled_jobs_controller.rb +5 -5
  18. data/app/helpers/solid_stack_web/application_helper.rb +30 -17
  19. data/app/views/layouts/solid_stack_web/application.html.erb +28 -24
  20. data/app/views/solid_stack_web/audit/index.html.erb +18 -18
  21. data/app/views/solid_stack_web/cable/index.html.erb +22 -19
  22. data/app/views/solid_stack_web/cable_messages/index.html.erb +15 -14
  23. data/app/views/solid_stack_web/cache/index.html.erb +19 -19
  24. data/app/views/solid_stack_web/cache_entries/index.html.erb +16 -15
  25. data/app/views/solid_stack_web/cache_entries/show.html.erb +11 -11
  26. data/app/views/solid_stack_web/dashboard/index.html.erb +54 -33
  27. data/app/views/solid_stack_web/errors/internal_server_error.html.erb +4 -4
  28. data/app/views/solid_stack_web/errors/not_found.html.erb +4 -4
  29. data/app/views/solid_stack_web/failed_jobs/destroy.turbo_stream.erb +2 -2
  30. data/app/views/solid_stack_web/failed_jobs/errors/index.html.erb +10 -10
  31. data/app/views/solid_stack_web/failed_jobs/index.html.erb +22 -22
  32. data/app/views/solid_stack_web/failed_jobs/show.html.erb +16 -16
  33. data/app/views/solid_stack_web/history/index.html.erb +19 -18
  34. data/app/views/solid_stack_web/jobs/_empty.html.erb +8 -8
  35. data/app/views/solid_stack_web/jobs/index.html.erb +35 -34
  36. data/app/views/solid_stack_web/jobs/show.html.erb +16 -16
  37. data/app/views/solid_stack_web/processes/index.html.erb +8 -8
  38. data/app/views/solid_stack_web/queues/index.html.erb +13 -13
  39. data/app/views/solid_stack_web/queues/show.html.erb +16 -16
  40. data/app/views/solid_stack_web/recurring_tasks/index.html.erb +16 -16
  41. data/app/views/solid_stack_web/shared/_locale_switcher.html.erb +14 -0
  42. data/app/views/solid_stack_web/stats/index.html.erb +13 -13
  43. data/config/locales/en.yml +395 -0
  44. data/config/locales/es.yml +395 -0
  45. data/lib/solid_stack_web/engine.rb +1 -0
  46. data/lib/solid_stack_web/version.rb +1 -1
  47. data/lib/solid_stack_web.rb +14 -1
  48. metadata +4 -1
@@ -1,30 +1,30 @@
1
1
  <div class="sqw-page-header sqw-page-header--split">
2
- <h1 class="sqw-page-title">Jobs</h1>
2
+ <h1 class="sqw-page-title"><%= t("solid_stack_web.jobs.title") %></h1>
3
3
  <div class="sqw-header-actions">
4
- <%= link_to "Export CSV", jobs_path(format: :csv, status: @status, q: @search, queue: @queue, period: @period, priority: @priority, sort: @sort, direction: @direction),
4
+ <%= link_to t("solid_stack_web.jobs.export_csv"), jobs_path(format: :csv, status: @status, q: @search, queue: @queue, period: @period, priority: @priority, sort: @sort, direction: @direction),
5
5
  class: "sqw-btn sqw-btn--muted sqw-btn--sm", data: { turbo: false } %>
6
6
  <% if @status == "scheduled" && @executions&.any? %>
7
- <%= button_to "Run All Now (#{@pagy.count})",
7
+ <%= button_to t("solid_stack_web.jobs.run_all_now", count: @pagy.count),
8
8
  run_all_now_scheduled_jobs_path(period: @period),
9
9
  method: :post,
10
10
  class: "sqw-btn sqw-btn--sm",
11
- data: { turbo_confirm: "Run all #{@pagy.count} scheduled jobs immediately?",
11
+ data: { turbo_confirm: t("solid_stack_web.jobs.confirm_run_all_now", count: @pagy.count),
12
12
  turbo_frame: "_top" } %>
13
13
  <% end %>
14
14
  <% if SolidStackWeb::Job::DISCARDABLE.include?(@status) && @executions&.any? %>
15
- <%= button_to "Discard All (#{@pagy.count})",
15
+ <%= button_to t("solid_stack_web.jobs.discard_all", count: @pagy.count),
16
16
  discard_all_jobs_path(status: @status, q: @search, queue: @queue, period: @period, priority: @priority),
17
17
  method: :post,
18
18
  class: "sqw-btn sqw-btn--danger sqw-btn--sm",
19
- data: { turbo_confirm: "Discard all #{@pagy.count} jobs? This cannot be undone.",
19
+ data: { turbo_confirm: t("solid_stack_web.jobs.confirm_discard_all", count: @pagy.count),
20
20
  turbo_frame: "_top" } %>
21
21
  <% end %>
22
22
  </div>
23
23
  </div>
24
24
 
25
25
  <div class="sqw-tabs">
26
- <% SolidStackWeb::Job::TAB_LABELS.each do |status, label| %>
27
- <%= link_to label, jobs_path(status: status, q: @search, queue: @queue, period: @period, priority: @priority, sort: @sort, direction: @direction),
26
+ <% SolidStackWeb::Job::STATUSES.each do |status| %>
27
+ <%= link_to t("solid_stack_web.jobs.tab_#{status}"), jobs_path(status: status, q: @search, queue: @queue, period: @period, priority: @priority, sort: @sort, direction: @direction),
28
28
  class: "sqw-tab #{"sqw-tab--active" if @status == status}" %>
29
29
  <% end %>
30
30
  </div>
@@ -40,31 +40,32 @@
40
40
  <%= hidden_field_tag :sort, @sort %>
41
41
  <%= hidden_field_tag :direction, @direction %>
42
42
  <input class="sqw-search-input" type="search" name="q" value="<%= @search %>"
43
- placeholder="Filter by job class…" autocomplete="off" aria-label="Filter by job class"
43
+ placeholder="<%= t("solid_stack_web.jobs.placeholder_job_class") %>" autocomplete="off"
44
+ aria-label="<%= t("solid_stack_web.jobs.aria_filter_job_class") %>"
44
45
  data-action="input->search#filter">
45
46
  <% if @queue_options.size > 1 %>
46
- <select name="queue" class="sqw-select" aria-label="Filter by queue"
47
+ <select name="queue" class="sqw-select" aria-label="<%= t("solid_stack_web.jobs.aria_filter_queue") %>"
47
48
  data-action="change->search#select">
48
- <option value="">All queues</option>
49
+ <option value=""><%= t("solid_stack_web.jobs.all_queues") %></option>
49
50
  <% @queue_options.each do |q| %>
50
51
  <option value="<%= q %>" <%= "selected" if @queue == q %>><%= q %></option>
51
52
  <% end %>
52
53
  </select>
53
54
  <% end %>
54
55
  <% if @priority_options.size > 1 %>
55
- <select name="priority" class="sqw-select" aria-label="Filter by priority"
56
+ <select name="priority" class="sqw-select" aria-label="<%= t("solid_stack_web.jobs.aria_filter_priority") %>"
56
57
  data-action="change->search#select">
57
- <option value="">All priorities</option>
58
+ <option value=""><%= t("solid_stack_web.jobs.all_priorities") %></option>
58
59
  <% @priority_options.each do |p| %>
59
- <option value="<%= p %>" <%= "selected" if @priority.to_s == p.to_s %>>Priority <%= p %></option>
60
+ <option value="<%= p %>" <%= "selected" if @priority.to_s == p.to_s %>><%= t("solid_stack_web.jobs.priority_option", n: p) %></option>
60
61
  <% end %>
61
62
  </select>
62
63
  <% end %>
63
64
  <% if @search.present? || @queue.present? || @priority.present? %>
64
- <%= link_to "Clear", jobs_path(status: @status, period: @period, sort: @sort, direction: @direction), class: "sqw-btn sqw-btn--muted sqw-btn--sm" %>
65
+ <%= link_to t("solid_stack_web.shared.clear"), jobs_path(status: @status, period: @period, sort: @sort, direction: @direction), class: "sqw-btn sqw-btn--muted sqw-btn--sm" %>
65
66
  <% end %>
66
- <div class="sqw-period-filter" role="group" aria-label="Time period">
67
- <%= link_to "All", jobs_path(status: @status, q: @search, queue: @queue, priority: @priority, sort: @sort, direction: @direction),
67
+ <div class="sqw-period-filter" role="group" aria-label="<%= t("solid_stack_web.shared.period_filter.label") %>">
68
+ <%= link_to t("solid_stack_web.shared.period_filter.all"), jobs_path(status: @status, q: @search, queue: @queue, priority: @priority, sort: @sort, direction: @direction),
68
69
  class: "sqw-period-btn #{"sqw-period-btn--active" if @period.nil?}" %>
69
70
  <%= link_to "1h", jobs_path(status: @status, q: @search, queue: @queue, priority: @priority, period: "1h", sort: @sort, direction: @direction),
70
71
  class: "sqw-period-btn #{"sqw-period-btn--active" if @period == "1h"}" %>
@@ -80,7 +81,7 @@
80
81
  <div data-controller="<%= "selection" if SolidStackWeb::Job::DISCARDABLE.include?(@status) %>">
81
82
  <% if SolidStackWeb::Job::DISCARDABLE.include?(@status) %>
82
83
  <%= form_with url: job_selection_path, method: :delete, id: "job-selection-form",
83
- data: { turbo_confirm: "Discard selected jobs? This cannot be undone." } do |f| %>
84
+ data: { turbo_confirm: t("solid_stack_web.jobs.confirm_discard_selected") } do |f| %>
84
85
  <%= f.hidden_field :status, value: @status %>
85
86
  <%= f.hidden_field :q, value: @search %>
86
87
  <%= f.hidden_field :queue, value: @queue %>
@@ -91,10 +92,10 @@
91
92
  <% end %>
92
93
 
93
94
  <div class="sqw-selection-bar" data-selection-target="bar" style="display: none;">
94
- <span class="sqw-muted"><span data-selection-target="count">0</span> selected</span>
95
+ <span class="sqw-muted"><span data-selection-target="count">0</span> <%= t("solid_stack_web.jobs.selected_label") %></span>
95
96
  <button type="button" class="sqw-btn sqw-btn--danger sqw-btn--sm"
96
97
  data-action="click->selection#submit"
97
- data-selection-form-id-param="job-selection-form">Discard Selected</button>
98
+ data-selection-form-id-param="job-selection-form"><%= t("solid_stack_web.jobs.discard_selected") %></button>
98
99
  </div>
99
100
  <% end %>
100
101
 
@@ -102,18 +103,18 @@
102
103
  <thead>
103
104
  <tr>
104
105
  <% if SolidStackWeb::Job::DISCARDABLE.include?(@status) %>
105
- <th scope="col"><input type="checkbox" class="sqw-checkbox" aria-label="Select all"
106
+ <th scope="col"><input type="checkbox" class="sqw-checkbox" aria-label="<%= t("solid_stack_web.shared.select_all") %>"
106
107
  data-selection-target="selectAll"
107
108
  data-action="change->selection#selectAll"></th>
108
109
  <% end %>
109
110
  <% sort_url = ->(p) { jobs_path(status: @status, q: @search, queue: @queue, period: @period, priority: @priority, **p) } %>
110
- <%= sort_header_th("Job Class", "class_name", sort_url, current_sort: @sort, current_dir: @direction) %>
111
- <%= sort_header_th("Queue", "queue_name", sort_url, current_sort: @sort, current_dir: @direction) %>
112
- <%= sort_header_th("Priority", "priority", sort_url, current_sort: @sort, current_dir: @direction) %>
113
- <%= sort_header_th("Enqueued At", "created_at", sort_url, current_sort: @sort, current_dir: @direction) %>
114
- <% if @status == "scheduled" %><th scope="col">Scheduled At</th><% end %>
115
- <% if @status == "claimed" %><th scope="col">Wait Time</th><% end %>
116
- <th scope="col"><span class="sqw-sr-only">Actions</span></th>
111
+ <%= sort_header_th(t("solid_stack_web.jobs.col_job_class"), "class_name", sort_url, current_sort: @sort, current_dir: @direction) %>
112
+ <%= sort_header_th(t("solid_stack_web.jobs.col_queue"), "queue_name", sort_url, current_sort: @sort, current_dir: @direction) %>
113
+ <%= sort_header_th(t("solid_stack_web.jobs.col_priority"), "priority", sort_url, current_sort: @sort, current_dir: @direction) %>
114
+ <%= sort_header_th(t("solid_stack_web.jobs.col_enqueued_at"), "created_at", sort_url, current_sort: @sort, current_dir: @direction) %>
115
+ <% if @status == "scheduled" %><th scope="col"><%= t("solid_stack_web.jobs.col_scheduled_at") %></th><% end %>
116
+ <% if @status == "claimed" %><th scope="col"><%= t("solid_stack_web.jobs.col_wait_time") %></th><% end %>
117
+ <th scope="col"><span class="sqw-sr-only"><%= t("solid_stack_web.shared.actions") %></span></th>
117
118
  </tr>
118
119
  </thead>
119
120
  <tbody>
@@ -138,11 +139,11 @@
138
139
  <% end %>
139
140
  <td class="sqw-actions">
140
141
  <% if @status == "scheduled" %>
141
- <%= button_to "Run Now", scheduled_job_path(execution),
142
+ <%= button_to t("solid_stack_web.jobs.run_now"), scheduled_job_path(execution),
142
143
  method: :patch,
143
144
  params: { offset: "now", period: @period },
144
145
  class: "sqw-btn sqw-btn--sm",
145
- data: { turbo_confirm: "Run this job immediately?" } %>
146
+ data: { turbo_confirm: t("solid_stack_web.jobs.confirm_run_now") } %>
146
147
  <% %w[1h 24h 7d].each do |offset| %>
147
148
  <%= button_to "+#{offset}", scheduled_job_path(execution),
148
149
  method: :patch,
@@ -151,19 +152,19 @@
151
152
  <% end %>
152
153
  <% end %>
153
154
  <% if %w[ready scheduled blocked].include?(@status) %>
154
- <%= button_to "Discard", job_path(execution, status: @status, q: @search, queue: @queue, period: @period, priority: @priority, sort: @sort, direction: @direction),
155
+ <%= button_to t("solid_stack_web.jobs.discard"), job_path(execution, status: @status, q: @search, queue: @queue, period: @period, priority: @priority, sort: @sort, direction: @direction),
155
156
  method: :delete, class: "sqw-btn sqw-btn--danger sqw-btn--sm",
156
- data: { turbo_confirm: "Discard this job?" } %>
157
+ data: { turbo_confirm: t("solid_stack_web.jobs.confirm_discard") } %>
157
158
  <% end %>
158
159
  </td>
159
160
  </tr>
160
161
  <% end %>
161
162
  </tbody>
162
163
  </table>
163
- <%== @pagy.series_nav(aria_label: "Pagination") if @pagy.pages > 1 %>
164
+ <%== @pagy.series_nav(aria_label: t("solid_stack_web.shared.pagination")) if @pagy.pages > 1 %>
164
165
  </div>
165
166
  <% else %>
166
167
  <%= render "empty" %>
167
168
  <% end %>
168
169
  </div>
169
- <% end %>
170
+ <% end %>
@@ -1,57 +1,57 @@
1
1
  <div class="sqw-page-header sqw-page-header--split">
2
2
  <div>
3
3
  <div class="sqw-breadcrumb">
4
- <%= link_to "Jobs", jobs_path(status: params[:status]) %> &rsaquo; Detail
4
+ <%= link_to t("solid_stack_web.jobs.title"), jobs_path(status: params[:status]) %> &rsaquo; <%= t("solid_stack_web.jobs.detail") %>
5
5
  </div>
6
6
  <h1 class="sqw-page-title sqw-monospace"><%= @execution.job.class_name %></h1>
7
7
  </div>
8
8
 
9
9
  <% if SolidStackWeb::Job::DISCARDABLE.include?(@status) %>
10
10
  <div class="sqw-detail-actions">
11
- <%= button_to "Discard Job", job_path(@execution.id, status: @status),
11
+ <%= button_to t("solid_stack_web.jobs.discard_job"), job_path(@execution.id, status: @status),
12
12
  method: :delete, class: "sqw-btn sqw-btn--danger",
13
- data: { turbo_confirm: "Discard this job?" } %>
13
+ data: { turbo_confirm: t("solid_stack_web.jobs.confirm_discard") } %>
14
14
  </div>
15
15
  <% end %>
16
16
  </div>
17
17
 
18
18
  <div class="sqw-detail-grid">
19
19
  <div class="sqw-detail-card sqw-detail-section">
20
- <h2 class="sqw-section-title">Details</h2>
20
+ <h2 class="sqw-section-title"><%= t("solid_stack_web.jobs.section_details") %></h2>
21
21
  <dl class="sqw-dl">
22
- <dt>Status</dt>
23
- <dd><span class="sqw-badge sqw-badge--<%= @status %>"><%= SolidStackWeb::Job::TAB_LABELS[@status] %></span></dd>
22
+ <dt><%= t("solid_stack_web.jobs.field_status") %></dt>
23
+ <dd><span class="sqw-badge sqw-badge--<%= @status %>"><%= t("solid_stack_web.jobs.tab_#{@status}") %></span></dd>
24
24
 
25
- <dt>Queue</dt>
25
+ <dt><%= t("solid_stack_web.jobs.field_queue") %></dt>
26
26
  <dd class="sqw-monospace"><%= @execution.job.queue_name %></dd>
27
27
 
28
- <dt>Priority</dt>
28
+ <dt><%= t("solid_stack_web.jobs.field_priority") %></dt>
29
29
  <dd><%= @execution.job.priority %></dd>
30
30
 
31
- <dt>Active Job ID</dt>
31
+ <dt><%= t("solid_stack_web.jobs.field_active_job_id") %></dt>
32
32
  <dd class="sqw-monospace sqw-truncate" title="<%= @execution.job.active_job_id %>"><%= @execution.job.active_job_id.presence || "—" %></dd>
33
33
 
34
- <dt>Concurrency Key</dt>
34
+ <dt><%= t("solid_stack_web.jobs.field_concurrency_key") %></dt>
35
35
  <dd class="sqw-monospace"><%= @execution.job.concurrency_key.presence || "—" %></dd>
36
36
 
37
37
  <% if @status == "blocked" %>
38
- <dt>Blocked Until</dt>
38
+ <dt><%= t("solid_stack_web.jobs.field_blocked_until") %></dt>
39
39
  <dd class="sqw-monospace"><%= local_time(@execution.expires_at, format: :long) %></dd>
40
40
  <% end %>
41
41
 
42
- <dt>Enqueued At</dt>
42
+ <dt><%= t("solid_stack_web.jobs.field_enqueued_at") %></dt>
43
43
  <dd class="sqw-monospace"><%= local_time(@execution.job.created_at, format: :long) %></dd>
44
44
 
45
- <dt>Scheduled At</dt>
45
+ <dt><%= t("solid_stack_web.jobs.field_scheduled_at") %></dt>
46
46
  <dd class="sqw-monospace"><%= local_time(@execution.job.scheduled_at, format: :long) %></dd>
47
47
 
48
- <dt>Finished At</dt>
48
+ <dt><%= t("solid_stack_web.jobs.field_finished_at") %></dt>
49
49
  <dd class="sqw-monospace"><%= local_time(@execution.job.finished_at, format: :long) %></dd>
50
50
  </dl>
51
51
  </div>
52
52
 
53
53
  <div class="sqw-detail-card sqw-detail-section">
54
- <h2 class="sqw-section-title">Arguments</h2>
54
+ <h2 class="sqw-section-title"><%= t("solid_stack_web.jobs.section_arguments") %></h2>
55
55
  <pre class="sqw-code-block"><%= @arguments ? JSON.pretty_generate(@arguments) : (@execution.job.arguments || "—") %></pre>
56
56
  </div>
57
- </div>
57
+ </div>
@@ -1,18 +1,18 @@
1
1
  <%= turbo_frame_tag "sqw-processes", target: "_top",
2
2
  data: { controller: "refresh", refresh_interval_value: SolidStackWeb.default_refresh_interval } do %>
3
3
  <div class="sqw-page-header">
4
- <h1 class="sqw-page-title">Processes</h1>
4
+ <h1 class="sqw-page-title"><%= t("solid_stack_web.processes.title") %></h1>
5
5
  </div>
6
6
 
7
7
  <% if @processes.any? %>
8
8
  <table class="sqw-table">
9
9
  <thead>
10
10
  <tr>
11
- <th scope="col">Kind</th>
12
- <th scope="col">Name</th>
13
- <th scope="col">PID</th>
14
- <th scope="col">Host</th>
15
- <th scope="col">Last Heartbeat</th>
11
+ <th scope="col"><%= t("solid_stack_web.processes.col_kind") %></th>
12
+ <th scope="col"><%= t("solid_stack_web.processes.col_name") %></th>
13
+ <th scope="col"><%= t("solid_stack_web.processes.col_pid") %></th>
14
+ <th scope="col"><%= t("solid_stack_web.processes.col_host") %></th>
15
+ <th scope="col"><%= t("solid_stack_web.processes.col_last_heartbeat") %></th>
16
16
  </tr>
17
17
  </thead>
18
18
  <tbody>
@@ -29,8 +29,8 @@
29
29
  </table>
30
30
  <% else %>
31
31
  <div class="sqw-empty">
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>
32
+ <p class="sqw-empty__title"><%= t("solid_stack_web.processes.empty_title") %></p>
33
+ <p class="sqw-empty__hint"><%= t("solid_stack_web.processes.empty_hint") %></p>
34
34
  </div>
35
35
  <% end %>
36
36
  <% end %>
@@ -1,16 +1,16 @@
1
1
  <div class="sqw-page-header">
2
- <h1 class="sqw-page-title">Queues</h1>
2
+ <h1 class="sqw-page-title"><%= t("solid_stack_web.queues.title") %></h1>
3
3
  </div>
4
4
 
5
5
  <% if @queues.any? %>
6
6
  <table class="sqw-table">
7
7
  <thead>
8
8
  <tr>
9
- <th scope="col">Name</th>
10
- <th scope="col">Size</th>
11
- <th scope="col">Depth (12h)</th>
12
- <th scope="col">Status</th>
13
- <th scope="col"><span class="sqw-sr-only">Actions</span></th>
9
+ <th scope="col"><%= t("solid_stack_web.queues.col_name") %></th>
10
+ <th scope="col"><%= t("solid_stack_web.queues.col_size") %></th>
11
+ <th scope="col"><%= t("solid_stack_web.queues.col_depth") %></th>
12
+ <th scope="col"><%= t("solid_stack_web.queues.col_status") %></th>
13
+ <th scope="col"><span class="sqw-sr-only"><%= t("solid_stack_web.shared.actions") %></span></th>
14
14
  </tr>
15
15
  </thead>
16
16
  <tbody>
@@ -24,17 +24,17 @@
24
24
  </td>
25
25
  <td>
26
26
  <% if queue[:paused] %>
27
- <span class="sqw-badge sqw-badge--paused">Paused</span>
27
+ <span class="sqw-badge sqw-badge--paused"><%= t("solid_stack_web.queues.status_paused") %></span>
28
28
  <% else %>
29
- <span class="sqw-badge sqw-badge--ready">Running</span>
29
+ <span class="sqw-badge sqw-badge--ready"><%= t("solid_stack_web.queues.status_running") %></span>
30
30
  <% end %>
31
31
  </td>
32
32
  <td class="sqw-actions">
33
33
  <% if queue[:paused] %>
34
- <%= button_to "Resume", queue_pause_path(queue[:name]),
34
+ <%= button_to t("solid_stack_web.queues.resume"), queue_pause_path(queue[:name]),
35
35
  method: :delete, class: "sqw-btn sqw-btn--sm" %>
36
36
  <% else %>
37
- <%= button_to "Pause", queue_pause_path(queue[:name]),
37
+ <%= button_to t("solid_stack_web.queues.pause"), queue_pause_path(queue[:name]),
38
38
  method: :post, class: "sqw-btn sqw-btn--sm" %>
39
39
  <% end %>
40
40
  </td>
@@ -44,7 +44,7 @@
44
44
  </table>
45
45
  <% else %>
46
46
  <div class="sqw-empty">
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>
47
+ <p class="sqw-empty__title"><%= t("solid_stack_web.queues.empty_title") %></p>
48
+ <p class="sqw-empty__hint"><%= t("solid_stack_web.queues.empty_hint") %> <%= link_to t("solid_stack_web.queues.check_jobs"), jobs_path %></p>
49
49
  </div>
50
- <% end %>
50
+ <% end %>
@@ -1,31 +1,31 @@
1
1
  <div class="sqw-page-header sqw-page-header--split">
2
2
  <div>
3
3
  <div class="sqw-breadcrumb">
4
- <%= link_to "Queues", queues_path %> &rsaquo; <%= @queue_name %>
4
+ <%= link_to t("solid_stack_web.queues.title"), queues_path %> &rsaquo; <%= @queue_name %>
5
5
  </div>
6
6
  <div class="sqw-page-title-row">
7
7
  <h1 class="sqw-page-title sqw-monospace"><%= @queue_name %></h1>
8
8
  <% if @paused %>
9
- <span class="sqw-badge sqw-badge--paused">Paused</span>
9
+ <span class="sqw-badge sqw-badge--paused"><%= t("solid_stack_web.queues.status_paused") %></span>
10
10
  <% else %>
11
- <span class="sqw-badge sqw-badge--ready">Running</span>
11
+ <span class="sqw-badge sqw-badge--ready"><%= t("solid_stack_web.queues.status_running") %></span>
12
12
  <% end %>
13
13
  </div>
14
14
  </div>
15
15
  <div class="sqw-header-actions">
16
16
  <% if @paused %>
17
- <%= button_to "Resume", queue_pause_path(@queue_name),
17
+ <%= button_to t("solid_stack_web.queues.resume"), queue_pause_path(@queue_name),
18
18
  method: :delete, class: "sqw-btn sqw-btn--sm" %>
19
19
  <% else %>
20
- <%= button_to "Pause", queue_pause_path(@queue_name),
20
+ <%= button_to t("solid_stack_web.queues.pause"), queue_pause_path(@queue_name),
21
21
  method: :post, class: "sqw-btn sqw-btn--sm" %>
22
22
  <% end %>
23
23
  <% if @executions.any? %>
24
- <%= button_to "Discard All Ready (#{@pagy.count})",
24
+ <%= button_to t("solid_stack_web.queues.discard_all_ready", count: @pagy.count),
25
25
  discard_all_jobs_path(status: "ready", queue: @queue_name),
26
26
  method: :post,
27
27
  class: "sqw-btn sqw-btn--danger sqw-btn--sm",
28
- data: { turbo_confirm: "Discard all #{@pagy.count} ready jobs in #{@queue_name}? This cannot be undone.",
28
+ data: { turbo_confirm: t("solid_stack_web.queues.confirm_discard_all_ready", count: @pagy.count, queue: @queue_name),
29
29
  turbo_frame: "_top" } %>
30
30
  <% end %>
31
31
  </div>
@@ -35,10 +35,10 @@
35
35
  <table class="sqw-table">
36
36
  <thead>
37
37
  <tr>
38
- <th scope="col">Job Class</th>
39
- <th scope="col">Priority</th>
40
- <th scope="col">Enqueued At</th>
41
- <th scope="col"><span class="sqw-sr-only">Actions</span></th>
38
+ <th scope="col"><%= t("solid_stack_web.queues.show_col_job_class") %></th>
39
+ <th scope="col"><%= t("solid_stack_web.queues.show_col_priority") %></th>
40
+ <th scope="col"><%= t("solid_stack_web.queues.show_col_enqueued_at") %></th>
41
+ <th scope="col"><span class="sqw-sr-only"><%= t("solid_stack_web.shared.actions") %></span></th>
42
42
  </tr>
43
43
  </thead>
44
44
  <tbody>
@@ -51,17 +51,17 @@
51
51
  <td><%= execution.job.priority %></td>
52
52
  <td class="sqw-muted"><%= local_time(execution.created_at) %></td>
53
53
  <td class="sqw-actions">
54
- <%= button_to "Discard", job_path(execution, status: "ready", queue: @queue_name),
54
+ <%= button_to t("solid_stack_web.queues.discard"), job_path(execution, status: "ready", queue: @queue_name),
55
55
  method: :delete, class: "sqw-btn sqw-btn--danger sqw-btn--sm",
56
- data: { turbo_confirm: "Discard this job?" } %>
56
+ data: { turbo_confirm: t("solid_stack_web.queues.confirm_discard") } %>
57
57
  </td>
58
58
  </tr>
59
59
  <% end %>
60
60
  </tbody>
61
61
  </table>
62
- <%== @pagy.series_nav(aria_label: "Pagination") if @pagy.pages > 1 %>
62
+ <%== @pagy.series_nav(aria_label: t("solid_stack_web.shared.pagination")) if @pagy.pages > 1 %>
63
63
  <% else %>
64
64
  <div class="sqw-empty">
65
- <p>No ready jobs in <strong><%= @queue_name %></strong>.</p>
65
+ <p><%= t("solid_stack_web.queues.no_ready_jobs", queue: @queue_name) %></p>
66
66
  </div>
67
- <% end %>
67
+ <% end %>
@@ -1,19 +1,19 @@
1
1
  <div class="sqw-page-header">
2
- <h1 class="sqw-page-title">Recurring Tasks</h1>
2
+ <h1 class="sqw-page-title"><%= t("solid_stack_web.recurring_tasks.title") %></h1>
3
3
  </div>
4
4
 
5
5
  <% if @recurring_tasks.any? %>
6
6
  <table class="sqw-table">
7
7
  <thead>
8
8
  <tr>
9
- <th scope="col">Key</th>
10
- <th scope="col">Schedule</th>
11
- <th scope="col">Job / Command</th>
12
- <th scope="col">Queue</th>
13
- <th scope="col">Next Run</th>
14
- <th scope="col">Last Run</th>
15
- <th scope="col">Type</th>
16
- <th scope="col"><span class="sqw-sr-only">Actions</span></th>
9
+ <th scope="col"><%= t("solid_stack_web.recurring_tasks.col_key") %></th>
10
+ <th scope="col"><%= t("solid_stack_web.recurring_tasks.col_schedule") %></th>
11
+ <th scope="col"><%= t("solid_stack_web.recurring_tasks.col_job_command") %></th>
12
+ <th scope="col"><%= t("solid_stack_web.recurring_tasks.col_queue") %></th>
13
+ <th scope="col"><%= t("solid_stack_web.recurring_tasks.col_next_run") %></th>
14
+ <th scope="col"><%= t("solid_stack_web.recurring_tasks.col_last_run") %></th>
15
+ <th scope="col"><%= t("solid_stack_web.recurring_tasks.col_type") %></th>
16
+ <th scope="col"><span class="sqw-sr-only"><%= t("solid_stack_web.shared.actions") %></span></th>
17
17
  </tr>
18
18
  </thead>
19
19
  <tbody>
@@ -44,16 +44,16 @@
44
44
  </td>
45
45
  <td>
46
46
  <% if task.static? %>
47
- <span class="sqw-badge sqw-badge--scheduled">Static</span>
47
+ <span class="sqw-badge sqw-badge--scheduled"><%= t("solid_stack_web.recurring_tasks.type_static") %></span>
48
48
  <% else %>
49
- <span class="sqw-badge sqw-badge--blocked">Dynamic</span>
49
+ <span class="sqw-badge sqw-badge--blocked"><%= t("solid_stack_web.recurring_tasks.type_dynamic") %></span>
50
50
  <% end %>
51
51
  </td>
52
52
  <td class="sqw-actions">
53
- <%= button_to "Run Now", recurring_task_run_path(task.key),
53
+ <%= button_to t("solid_stack_web.recurring_tasks.run_now"), recurring_task_run_path(task.key),
54
54
  method: :post,
55
55
  class: "sqw-btn sqw-btn--sm",
56
- data: { turbo_confirm: "Run \"#{task.key}\" immediately?" } %>
56
+ data: { turbo_confirm: t("solid_stack_web.recurring_tasks.confirm_run_now", key: task.key) } %>
57
57
  </td>
58
58
  </tr>
59
59
  <% end %>
@@ -61,7 +61,7 @@
61
61
  </table>
62
62
  <% else %>
63
63
  <div class="sqw-empty">
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>
64
+ <p class="sqw-empty__title"><%= t("solid_stack_web.recurring_tasks.empty_title") %></p>
65
+ <p class="sqw-empty__hint"><%= t("solid_stack_web.recurring_tasks.empty_hint") %></p>
66
66
  </div>
67
- <% end %>
67
+ <% end %>
@@ -0,0 +1,14 @@
1
+ <% if SolidStackWeb.available_locales.size > 1 %>
2
+ <form class="sqw-locale-form" action="<%= request.path %>" method="get" aria-label="<%= t("solid_stack_web.locale_switcher.label") %>">
3
+ <% request.query_parameters.except("locale").each do |k, v| %>
4
+ <%= hidden_field_tag k, v %>
5
+ <% end %>
6
+ <select name="locale" class="sqw-locale-select"
7
+ onchange="this.form.submit()"
8
+ aria-label="<%= t("solid_stack_web.locale_switcher.label") %>">
9
+ <% SolidStackWeb.available_locales.each do |loc| %>
10
+ <option value="<%= loc %>" <%= "selected" if I18n.locale.to_s == loc.to_s %>><%= loc.to_s.upcase %></option>
11
+ <% end %>
12
+ </select>
13
+ </form>
14
+ <% end %>
@@ -1,5 +1,5 @@
1
1
  <div class="sqw-page-header">
2
- <h1 class="sqw-page-title">Performance Stats</h1>
2
+ <h1 class="sqw-page-title"><%= t("solid_stack_web.stats.title") %></h1>
3
3
  </div>
4
4
 
5
5
  <% if @stats.any? %>
@@ -7,15 +7,15 @@
7
7
  <thead>
8
8
  <tr>
9
9
  <% [
10
- ["class_name", "Job Class"],
11
- ["count", "Executions"],
12
- ["avg", "Avg"],
13
- ["p50", "p50"],
14
- ["p95", "p95"],
15
- ["p99", "p99"],
16
- ["stddev", "Std Dev"],
17
- ["min", "Min"],
18
- ["max", "Max"]
10
+ ["class_name", t("solid_stack_web.stats.col_job_class")],
11
+ ["count", t("solid_stack_web.stats.col_executions")],
12
+ ["avg", t("solid_stack_web.stats.col_avg")],
13
+ ["p50", t("solid_stack_web.stats.col_p50")],
14
+ ["p95", t("solid_stack_web.stats.col_p95")],
15
+ ["p99", t("solid_stack_web.stats.col_p99")],
16
+ ["stddev", t("solid_stack_web.stats.col_std_dev")],
17
+ ["min", t("solid_stack_web.stats.col_min")],
18
+ ["max", t("solid_stack_web.stats.col_max")]
19
19
  ].each do |col, label| %>
20
20
  <th scope="col" <%= "aria-sort=\"#{@direction == 'asc' ? 'ascending' : 'descending'}\"".html_safe if @sort == col %>>
21
21
  <% next_dir = (@sort == col && @direction == "desc") ? "asc" : "desc" %>
@@ -47,7 +47,7 @@
47
47
  </table>
48
48
  <% else %>
49
49
  <div class="sqw-empty">
50
- <p class="sqw-empty__title">No finished jobs yet</p>
51
- <p class="sqw-empty__hint">Performance stats appear here once jobs complete. <%= link_to "View active jobs →", jobs_path %></p>
50
+ <p class="sqw-empty__title"><%= t("solid_stack_web.stats.empty_title") %></p>
51
+ <p class="sqw-empty__hint"><%= t("solid_stack_web.stats.empty_hint") %> <%= link_to t("solid_stack_web.stats.view_active_jobs"), jobs_path %></p>
52
52
  </div>
53
- <% end %>
53
+ <% end %>