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 +4 -4
- data/CHANGELOG.md +19 -0
- data/README.md +11 -5
- data/app/controllers/i18n_feedback/suggestions_controller.rb +2 -20
- data/app/views/i18n_feedback/suggestions/index.html.erb +5 -21
- data/app/views/layouts/i18n_feedback/application.html.erb +1 -4
- data/config/locales/i18n_feedback.en.yml +2 -6
- data/config/routes.rb +5 -3
- data/lib/i18n_feedback/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 46eee60f6c5765728de028eb4cc020adf87c05fbfa1250a2b3bb9eccc5d66ad2
|
|
4
|
+
data.tar.gz: d7732101bdf49c68868f4f3580309ac0cb9e040024bdbd33e4c9598cb7a1dd0a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
###
|
|
240
|
+
### Review dashboard
|
|
241
241
|
|
|
242
242
|
Mounted at your `mount_path` (default `/i18n_feedback`), the engine root is a
|
|
243
|
-
built-in
|
|
244
|
-
per-locale filter, each suggestion shown as current-vs-proposed
|
|
245
|
-
|
|
246
|
-
|
|
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
|
|
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:
|
|
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
|
-
<
|
|
20
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
5
|
-
#
|
|
6
|
-
|
|
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
|
|