i18n_feedback 0.8.0 → 0.8.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 675e56ff8164412361ecd15886a56ef5eb7a97fdda9a766246b0d690c602d2e1
4
- data.tar.gz: 36c511550f1b042de8052e995afeab43c27645410d0dea5246a4ffde9e9d598f
3
+ metadata.gz: 46eee60f6c5765728de028eb4cc020adf87c05fbfa1250a2b3bb9eccc5d66ad2
4
+ data.tar.gz: d7732101bdf49c68868f4f3580309ac0cb9e040024bdbd33e4c9598cb7a1dd0a
5
5
  SHA512:
6
- metadata.gz: fc37ff253e157cc2c2b619e8007bdbe698a1669a96c19c5243b5e326db8185344ee1fa49bdaf40f355240fb6322d90d84bc2e7944bef84b19c77bc381bdcf859
7
- data.tar.gz: f9136776bb4abeb8bb1a6271e8f924178486fad4ab3923eed3e9826110daa56501ca8773962ffc8caee9b340c9b294c6747b500c38b989ed6e412ada137d6237
6
+ metadata.gz: 93b6367c3e3b3479333152301dd6ab44e39d2fe734b10374fef7193d32652d18d1a13042820fa484d0e187061a6f0bda4a15ce3830f97ed409eeea25dfc02137
7
+ data.tar.gz: 9eb009039394424338293e77e1b5d684e75528508a5889856b710935ea34029300347218563a033025783fe46ffc100f5dd4e86cf5a62c3f788858474ad14a90
data/CHANGELOG.md CHANGED
@@ -2,6 +2,25 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.8.2]
6
+
7
+ - Fix the dashboard's locale filter doing nothing under a strict (nonce-based)
8
+ Content-Security-Policy: it relied on an inline `onchange` handler, which such
9
+ a CSP blocks. The filter now has an always-visible **Filter** submit button, so
10
+ it works with JS off or inline handlers blocked (the `onchange` stays as a
11
+ nicety where allowed).
12
+ - Give the locale filter a visible **Locale** label.
13
+
14
+ ## [0.8.1]
15
+
16
+ - Make the dashboard **read-only**. Apply / Reject / Reopen / Delete only ever
17
+ flipped the stored `status` — they never touched the host's locale files, so
18
+ offering them implied a capability the gem doesn't have. The buttons and their
19
+ `update` / `destroy` routes and actions are removed; the dashboard now just
20
+ lists suggestions for review, and you make the edits in your own locale files.
21
+ A suggestion's `status` still exists on the model for out-of-band tracking and
22
+ as groundwork for a future "apply to locale file" feature.
23
+
5
24
  ## [0.8.0]
6
25
 
7
26
  - Add a built-in **triage dashboard**, mounted at the engine root (your
data/README.md CHANGED
@@ -237,13 +237,19 @@ blue accent stays the same in both.
237
237
 
238
238
  ## Reviewing suggestions
239
239
 
240
- ### Triage dashboard
240
+ ### Review dashboard
241
241
 
242
242
  Mounted at your `mount_path` (default `/i18n_feedback`), the engine root is a
243
- built-in triage dashboard: pending / applied / rejected tabs with counts, a
244
- per-locale filter, each suggestion shown as current-vs-proposed, and one-click
245
- **Apply** / **Reject** / **Reopen** / **Delete**. It's plain server-rendered
246
- HTML with its own styling — no host assets or JS framework needed.
243
+ built-in **read-only** review board: pending / applied / rejected tabs with
244
+ counts, a per-locale filter, and each suggestion shown as current-vs-proposed
245
+ with its comment and author. It's plain server-rendered HTML with its own
246
+ styling — no host assets or JS framework needed.
247
+
248
+ It is deliberately read-only. The gem never writes to your locale files, so it
249
+ doesn't pretend to: you review the suggestions here, then make the edits in your
250
+ own `config/locales/*.yml`. (A suggestion's `status` still exists on the model
251
+ for your own tracking — set it from the console — and is the groundwork for a
252
+ future "apply to locale file" feature.)
247
253
 
248
254
  It has its own gate, `config.authorize_admin`, **defaulting to development
249
255
  only** — so a fresh install never exposes it in production. Point it at your own
@@ -4,10 +4,9 @@ module I18nFeedback
4
4
  class SuggestionsController < ApplicationController
5
5
  PER_PAGE = 50
6
6
 
7
- # Public widget API is available-gated; the triage dashboard is admin-gated.
7
+ # Public widget API is available-gated; the read-only dashboard is admin-gated.
8
8
  before_action :require_available, only: %i[context create]
9
- before_action :require_admin, only: %i[index update destroy]
10
- before_action :set_suggestion, only: %i[update destroy]
9
+ before_action :require_admin, only: :index
11
10
 
12
11
  layout 'i18n_feedback/application', only: :index
13
12
 
@@ -40,19 +39,6 @@ module I18nFeedback
40
39
  @suggestions = rows.first(PER_PAGE)
41
40
  end
42
41
 
43
- def update
44
- # status arrives from a dashboard button; ignore anything not a real state
45
- # (an AR enum would otherwise raise on an unknown value).
46
- status = params[:status].to_s
47
- @suggestion.update!(status: status) if Suggestion::STATUSES.include?(status)
48
- redirect_back fallback_location: root_path, status: :see_other
49
- end
50
-
51
- def destroy
52
- @suggestion.destroy!
53
- redirect_back fallback_location: root_path, status: :see_other
54
- end
55
-
56
42
  # --- widget API (public) -------------------------------------------------
57
43
 
58
44
  # Pending suggestions for one key/locale, shown as read-only context when the
@@ -81,10 +67,6 @@ module I18nFeedback
81
67
 
82
68
  private
83
69
 
84
- def set_suggestion
85
- @suggestion = Suggestion.find(params[:id])
86
- end
87
-
88
70
  def attribute_author(suggestion)
89
71
  author = current_author
90
72
  return unless author
@@ -16,14 +16,16 @@
16
16
  <% if @locales.many? %>
17
17
  <form class="filters" method="get">
18
18
  <input type="hidden" name="status" value="<%= @status %>">
19
- <select name="locale" onchange="this.form.requestSubmit()"
20
- aria-label="<%= t('i18n_feedback.dashboard.filter', default: 'Filter by locale') %>">
19
+ <label for="i18nf-locale"><%= t('i18n_feedback.dashboard.locale', default: 'Locale') %></label>
20
+ <select id="i18nf-locale" name="locale" onchange="this.form.submit()">
21
21
  <option value=""><%= t('i18n_feedback.dashboard.all_locales', default: 'All locales') %></option>
22
22
  <% @locales.each do |locale| %>
23
23
  <option value="<%= locale %>" <%= 'selected' if locale == @locale %>><%= locale %></option>
24
24
  <% end %>
25
25
  </select>
26
- <noscript><button><%= t('i18n_feedback.dashboard.filter', default: 'Filter') %></button></noscript>
26
+ <%# Inline onchange auto-submits where allowed; this button is the reliable
27
+ path where a strict CSP blocks inline handlers, or with JS off. %>
28
+ <button><%= t('i18n_feedback.dashboard.filter', default: 'Filter') %></button>
27
29
  </form>
28
30
  <% end %>
29
31
 
@@ -53,24 +55,6 @@
53
55
  <% if suggestion.comment.present? %>
54
56
  <div class="comment" dir="auto"><%= suggestion.comment %></div>
55
57
  <% end %>
56
-
57
- <div class="actions">
58
- <% unless suggestion.status_applied? %>
59
- <%= button_to t('i18n_feedback.dashboard.apply', default: 'Apply'),
60
- suggestion_path(suggestion, status: 'applied'), method: :patch, class: 'primary' %>
61
- <% end %>
62
- <% unless suggestion.status_rejected? %>
63
- <%= button_to t('i18n_feedback.dashboard.reject', default: 'Reject'),
64
- suggestion_path(suggestion, status: 'rejected'), method: :patch %>
65
- <% end %>
66
- <% unless suggestion.status_pending? %>
67
- <%= button_to t('i18n_feedback.dashboard.reopen', default: 'Reopen'),
68
- suggestion_path(suggestion, status: 'pending'), method: :patch %>
69
- <% end %>
70
- <%= button_to t('i18n_feedback.dashboard.delete', default: 'Delete'),
71
- suggestion_path(suggestion), method: :delete, class: 'danger',
72
- form: { data: { turbo_confirm: t('i18n_feedback.dashboard.delete_confirm', default: 'Delete this suggestion?') } } %>
73
- </div>
74
58
  </article>
75
59
  <% end %>
76
60
 
@@ -43,6 +43,7 @@
43
43
  }
44
44
 
45
45
  .filters { margin-bottom: 16px; display: flex; gap: 8px; align-items: center; }
46
+ .filters label { color: var(--muted); font-size: 13px; }
46
47
  .filters select {
47
48
  padding: 6px 8px; border: 1px solid var(--border); border-radius: 8px;
48
49
  background: var(--surface); color: var(--text); font: inherit;
@@ -75,15 +76,11 @@
75
76
  dd .new { font-weight: 600; }
76
77
  .comment { margin-top: 10px; padding: 8px 10px; background: var(--code); border-radius: 8px; font-size: 14px; overflow-wrap: anywhere; }
77
78
 
78
- .actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 14px; }
79
- .actions form { display: inline; margin: 0; }
80
79
  button {
81
80
  padding: 7px 13px; border: 1px solid var(--border); border-radius: 8px; cursor: pointer;
82
81
  background: var(--surface); color: var(--text); font: inherit; font-size: 14px; font-weight: 600;
83
82
  }
84
83
  button:hover { border-color: var(--muted); }
85
- button.primary { background: var(--accent); border-color: var(--accent); color: var(--accent-text); }
86
- button.danger { color: var(--rejected); border-color: transparent; }
87
84
  :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
88
85
 
89
86
  .empty { padding: 48px 16px; text-align: center; color: var(--muted); border: 1px dashed var(--border); border-radius: 12px; }
@@ -24,16 +24,12 @@ en:
24
24
  dashboard:
25
25
  title: "Translation suggestions"
26
26
  subtitle: "Review what reviewers proposed, then apply it to your locale files."
27
- filter: "Filter by locale"
27
+ locale: "Locale"
28
+ filter: "Filter"
28
29
  all_locales: "All locales"
29
30
  current: "Current"
30
31
  suggested: "Suggested"
31
32
  ago: "ago"
32
- apply: "Apply"
33
- reject: "Reject"
34
- reopen: "Reopen"
35
- delete: "Delete"
36
- delete_confirm: "Delete this suggestion?"
37
33
  empty: "Nothing here yet."
38
34
  newer: "← Newer"
39
35
  older: "Older →"
data/config/routes.rb CHANGED
@@ -1,9 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  I18nFeedback::Engine.routes.draw do
4
- # index/update/destroy are the admin triage dashboard; create + the `context`
5
- # collection route are the widget's public API (see SuggestionsController).
6
- resources :suggestions, only: %i[index create update destroy] do
4
+ # index is the read-only admin dashboard; create + the `context` collection
5
+ # route are the widget's public API (see SuggestionsController). There is
6
+ # deliberately no update/destroy the tool never mutates suggestions from the
7
+ # UI, since it can't write to the host's locale files.
8
+ resources :suggestions, only: %i[index create] do
7
9
  get :context, on: :collection
8
10
  end
9
11
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module I18nFeedback
4
- VERSION = '0.8.0'
4
+ VERSION = '0.8.2'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: i18n_feedback
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.8.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yaroslav Shmarov