feat(feedback): admin detail subpage + respond + archive/restore #107

Merged
YousifShkara merged 1 commit from feat/feedback-admin-detail-and-archive into main 2026-06-11 05:38:50 +02:00
Owner

Closes BUNYIP-85. Second of three feedback parity PRs. Stacks on BUNYIP-84.

bunyip-api already implements every backend behaviour:

  • GET /v1/admin/feedback/{id} (handlers/feedback.rs:346-358)
  • POST /v1/admin/feedback/{id}/respond (handlers/feedback.rs:361-426; sends email server-side)
  • GET /v1/admin/feedback/archive (handlers/feedback.rs:545-558)
  • POST /v1/admin/feedback/archive/{archive_id}/restore (handlers/feedback.rs:560-581)

bunyip-web exposed none of them. Admins could see the row list and toggle status, but not read the full message, send a response, or work the archive.

Changes:

  • bunyip-web/src/api/types.rs: add ArchivedFeedback mirroring the API's ArchivedFeedbackItem. Add responded_at: Option<String> (serde-default for rolling deploys) to AdminFeedbackDetail so the detail view can show when a response was sent.
  • bunyip-web/src/api/admin.rs: four new helpers - feedback_detail, respond_to_feedback (POST {response, status: "responded"} JSON), feedback_archive, restore_feedback.
  • bunyip-web/src/handlers/admin.rs:
    • Existing feedback list grows an "Active / Archive" tab pair above the list; subject in each row becomes a link to the new detail subpage.
    • feedback_detail (GET /admin/feedback/:id) renders the detail view: unmasked email, full message (whitespace-pre-wrap), page_path, tags, current status, and EITHER an inline response form OR the existing admin response with its responded-at relative time.
    • feedback_respond (POST /admin/feedback/:id/respond) validates non-empty, calls the API helper, redirects back to the detail page with ?toast_ok=Response%20sent / ?toast_err=… so the existing toast plumbing renders the confirmation.
    • feedback_archive (GET /admin/feedback/:archive) paginates the archive with Restore buttons per row, sharing the same tab nav.
    • feedback_restore (POST /admin/feedback/archive/:archive_id/restore) calls the API and redirects back to the archive page with a toast.
  • bunyip-web/src/main.rs: register the four new routes. Archive routes register BEFORE the :id detail route so axum's matcher does not interpret the literal archive as a feedback id.

Attachments rendering on the detail page is intentionally deferred to BUNYIP-86 alongside the multipart-form-upload work, so the admin UX and the file-upload transport change ship as separate PRs.

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-85

Closes BUNYIP-85. Second of three feedback parity PRs. Stacks on BUNYIP-84. bunyip-api already implements every backend behaviour: - `GET /v1/admin/feedback/{id}` (handlers/feedback.rs:346-358) - `POST /v1/admin/feedback/{id}/respond` (handlers/feedback.rs:361-426; sends email server-side) - `GET /v1/admin/feedback/archive` (handlers/feedback.rs:545-558) - `POST /v1/admin/feedback/archive/{archive_id}/restore` (handlers/feedback.rs:560-581) bunyip-web exposed none of them. Admins could see the row list and toggle status, but not read the full message, send a response, or work the archive. Changes: - `bunyip-web/src/api/types.rs`: add `ArchivedFeedback` mirroring the API's `ArchivedFeedbackItem`. Add `responded_at: Option<String>` (serde-default for rolling deploys) to `AdminFeedbackDetail` so the detail view can show when a response was sent. - `bunyip-web/src/api/admin.rs`: four new helpers - `feedback_detail`, `respond_to_feedback` (POST `{response, status: "responded"}` JSON), `feedback_archive`, `restore_feedback`. - `bunyip-web/src/handlers/admin.rs`: - Existing feedback list grows an "Active / Archive" tab pair above the list; subject in each row becomes a link to the new detail subpage. - `feedback_detail` (GET `/admin/feedback/:id`) renders the detail view: unmasked email, full message (whitespace-pre-wrap), `page_path`, tags, current status, and EITHER an inline response form OR the existing admin response with its responded-at relative time. - `feedback_respond` (POST `/admin/feedback/:id/respond`) validates non-empty, calls the API helper, redirects back to the detail page with `?toast_ok=Response%20sent` / `?toast_err=…` so the existing toast plumbing renders the confirmation. - `feedback_archive` (GET `/admin/feedback/:archive`) paginates the archive with Restore buttons per row, sharing the same tab nav. - `feedback_restore` (POST `/admin/feedback/archive/:archive_id/restore`) calls the API and redirects back to the archive page with a toast. - `bunyip-web/src/main.rs`: register the four new routes. Archive routes register BEFORE the `:id` detail route so axum's matcher does not interpret the literal `archive` as a feedback id. Attachments rendering on the detail page is intentionally deferred to BUNYIP-86 alongside the multipart-form-upload work, so the admin UX and the file-upload transport change ship as separate PRs. `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-85
feat(feedback): admin detail subpage + respond + archive/restore
All checks were successful
Create release / Create release from merged PR (pull_request) Has been skipped
Check / fmt / clippy / build / test (pull_request) Successful in 1m0s
00f2a45fd6
Closes BUNYIP-85. Second of three feedback parity PRs. Stacks on BUNYIP-84.

bunyip-api already implements every backend behaviour:
- `GET  /v1/admin/feedback/{id}`                              (handlers/feedback.rs:346-358)
- `POST /v1/admin/feedback/{id}/respond`                      (handlers/feedback.rs:361-426; sends email server-side)
- `GET  /v1/admin/feedback/archive`                           (handlers/feedback.rs:545-558)
- `POST /v1/admin/feedback/archive/{archive_id}/restore`      (handlers/feedback.rs:560-581)

bunyip-web exposed none of them. Admins could see the row list and toggle status, but not read the full message, send a response, or work the archive.

Changes:

- `bunyip-web/src/api/types.rs`: add `ArchivedFeedback` mirroring the API's `ArchivedFeedbackItem`. Add `responded_at: Option<String>` (serde-default for rolling deploys) to `AdminFeedbackDetail` so the detail view can show when a response was sent.
- `bunyip-web/src/api/admin.rs`: four new helpers - `feedback_detail`, `respond_to_feedback` (POST `{response, status: "responded"}` JSON), `feedback_archive`, `restore_feedback`.
- `bunyip-web/src/handlers/admin.rs`:
  - Existing feedback list grows an "Active / Archive" tab pair above the list; subject in each row becomes a link to the new detail subpage.
  - `feedback_detail` (GET `/admin/feedback/:id`) renders the detail view: unmasked email, full message (whitespace-pre-wrap), `page_path`, tags, current status, and EITHER an inline response form OR the existing admin response with its responded-at relative time.
  - `feedback_respond` (POST `/admin/feedback/:id/respond`) validates non-empty, calls the API helper, redirects back to the detail page with `?toast_ok=Response%20sent` / `?toast_err=…` so the existing toast plumbing renders the confirmation.
  - `feedback_archive` (GET `/admin/feedback/:archive`) paginates the archive with Restore buttons per row, sharing the same tab nav.
  - `feedback_restore` (POST `/admin/feedback/archive/:archive_id/restore`) calls the API and redirects back to the archive page with a toast.
- `bunyip-web/src/main.rs`: register the four new routes. Archive routes register BEFORE the `:id` detail route so axum's matcher does not interpret the literal `archive` as a feedback id.

Attachments rendering on the detail page is intentionally deferred to BUNYIP-86 alongside the multipart-form-upload work, so the admin UX and the file-upload transport change ship as separate PRs.

`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-85
YousifShkara deleted branch feat/feedback-admin-detail-and-archive 2026-06-11 05:38:50 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
psa-systems/bunyip!107
No description provided.