Feedback inbox: read/unread + delete
Make the admin Feedback section a real inbox.
* DB: feedback.read_at column (schema + idempotent migration).
* API: feedback list returns read_at; POST /api/admin/feedback/{id}/read
{read} toggles it; DELETE /api/admin/feedback/{id} removes a message
(both admin-gated). admin_stats gains feedback_unread; the Attention strip
and the tab badge now count UNREAD, not total.
* Frontend: unread messages are highlighted with an accent rail + dot; an
Unread filter joins the category chips; each message has Mark read/unread
and Delete (confirm), with optimistic updates that revert on failure.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -57,12 +57,12 @@ def test_attention_flags_resting_flagged_and_brief():
|
||||
{"failures": 0, "review_flag": 1},
|
||||
{"failures": 0, "review_flag": 0},
|
||||
]
|
||||
items = queries._attention(content, sources, feedback_7d=2)
|
||||
items = queries._attention(content, sources, feedback_unread=2)
|
||||
texts = " | ".join(i["text"] for i in items)
|
||||
assert "1 source backing off" in texts # one resting
|
||||
assert "1 source flagged" in texts # one flagged
|
||||
assert "brief has only 5" in texts # brief < 7
|
||||
assert "2 feedback" in texts # recent feedback
|
||||
assert "2 unread feedback" in texts # unread feedback
|
||||
# 90/100 = 90% image coverage → no coverage warning
|
||||
assert "Image coverage" not in texts
|
||||
|
||||
@@ -71,4 +71,4 @@ def test_attention_clear_when_healthy():
|
||||
from goodnews import queries
|
||||
content = {"served": 100, "with_image": 95, "latest_brief_size": 7}
|
||||
sources = [{"failures": 0, "review_flag": 0}]
|
||||
assert queries._attention(content, sources, feedback_7d=0) == []
|
||||
assert queries._attention(content, sources, feedback_unread=0) == []
|
||||
|
||||
Reference in New Issue
Block a user