feat(admin-feedback): per-row Archive action + Reply button discoverability #115
No reviewers
Labels
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
psa-systems/bunyip!115
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/feedback-admin-archive-and-reply-button"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes BUNYIP-93. Stacks on BUNYIP-92.
Two complaints from the admin dashboard:
The Archive TAB shipped in BUNYIP-85 with a Restore button, but no path INTO the archive. The only existing archive mechanism was the batch background job
FeedbackRepository::archive_and_purge_closed(crates/bunyip-domain/src/repositories/feedback.rs:284), which moves status='closed' AND updated_at < NOW() - INTERVAL '90 days' rows intofeedback_archive. An admin wanting to archive a row immediately had no path; the existing batch job is non-interactive."There's no way to reply." The reply form has shipped since BUNYIP-85, but lives on the detail subpage; the row's clickable subject is the only way in and it wasn't obvious. The detail subpage at
/admin/feedback/:idhas the textarea + send button; the API sends an email to the submitter.Changes:
AuditAction::FeedbackArchived(serialises tofeedback_archived, listed as an admin action).archive_one(pool, id)method modelled on the existing batch SQL (INSERT INTO feedback_archive ... DELETE FROM feedback) but scoped to one id and wrapped in a transaction so a crash mid-way cannot duplicate or lose the row. ReturnsAppError::not_foundwhen the id is not present. Attachments cascade-delete with the source row, matching the existing batch path; restore (BUNYIP-85) does not bring attachments back. Existing trade-off; not changing it here.archive_feedbackhandler atPOST /v1/admin/feedback/{id}/archive. Writes aFeedbackArchivedaudit log row on success.archive_feedbackBFF helper./admin/feedback/:id, not a separate action - the reply form is the existing detail subpage. Hidden on Spam and Archive tabs (replying to spam is meaningless; archived rows are out of queue).feedback_archive_actionSSR handler atPOST /admin/feedback/:id/archive, follows the samefromhidden-field redirect pattern BUNYIP-92 introduced so the admin lands back on the originating tab with a?toast_ok=Archivedtoast.Naming: the existing
feedback_archiveGET handler (BUNYIP-85) lists archived rows. The newfeedback_archive_actionPOST handler archives a single row. The two names differ so the router and the reader can tell them apart at a glance.just check-containerclean (modulo the pre-existingtest_config_defaultsparallel-env-var flake on main; 188 other tests pass; fmt + clippy + build clean).#BUNYIP-93
Closes BUNYIP-93. Stacks on BUNYIP-92. Two complaints from the admin dashboard: 1. The Archive TAB shipped in BUNYIP-85 with a Restore button, but no path INTO the archive. The only existing archive mechanism was the batch background job `FeedbackRepository::archive_and_purge_closed` (`crates/bunyip-domain/src/repositories/feedback.rs:284`), which moves status='closed' AND updated_at < NOW() - INTERVAL '90 days' rows into `feedback_archive`. An admin wanting to archive a row immediately had no path; the existing batch job is non-interactive. 2. "There's no way to reply." The reply form has shipped since BUNYIP-85, but lives on the detail subpage; the row's clickable subject is the only way in and it wasn't obvious. The detail subpage at `/admin/feedback/:id` has the textarea + send button; the API sends an email to the submitter. Changes: - crates/bunyip-domain/src/models/audit.rs: add `AuditAction::FeedbackArchived` (serialises to `feedback_archived`, listed as an admin action). - crates/bunyip-domain/src/repositories/feedback.rs: new `archive_one(pool, id)` method modelled on the existing batch SQL (`INSERT INTO feedback_archive ... DELETE FROM feedback`) but scoped to one id and wrapped in a transaction so a crash mid-way cannot duplicate or lose the row. Returns `AppError::not_found` when the id is not present. Attachments cascade-delete with the source row, matching the existing batch path; restore (BUNYIP-85) does not bring attachments back. Existing trade-off; not changing it here. - bunyip-api/src/handlers/feedback.rs: new `archive_feedback` handler at `POST /v1/admin/feedback/{id}/archive`. Writes a `FeedbackArchived` audit log row on success. - bunyip-api/src/handlers/mod.rs + bunyip-api/src/routes/admin.rs: re-export and route registration adjacent to mark-spam / unmark-spam. - bunyip-web/src/api/admin.rs: new `archive_feedback` BFF helper. - bunyip-web/src/handlers/admin.rs: - Row template: an Archive button next to the existing tab-aware action group on Active / Closed / Spam tabs (Archive tab itself has Restore from BUNYIP-85, no Archive button needed). - Row template: a "Reply" anchor styled as a primary button on Active and Closed rows. It is a link to `/admin/feedback/:id`, not a separate action - the reply form is the existing detail subpage. Hidden on Spam and Archive tabs (replying to spam is meaningless; archived rows are out of queue). - Detail subpage gains an Archive button next to the existing Mark Spam + Delete buttons. - New `feedback_archive_action` SSR handler at `POST /admin/feedback/:id/archive`, follows the same `from` hidden-field redirect pattern BUNYIP-92 introduced so the admin lands back on the originating tab with a `?toast_ok=Archived` toast. - bunyip-web/src/main.rs: register the new route adjacent to mark-spam / unmark-spam / delete. Naming: the existing `feedback_archive` GET handler (BUNYIP-85) lists archived rows. The new `feedback_archive_action` POST handler archives a single row. The two names differ so the router and the reader can tell them apart at a glance. `just check-container` clean (modulo the pre-existing `test_config_defaults` parallel-env-var flake on main; 188 other tests pass; fmt + clippy + build clean). #BUNYIP-93