Admin-managed feedback categories

Categories move from a hard-coded list to a feedback_categories table
(migration 0062), seeded with the four existing keys, labels and
descriptions so production feedback is untouched; any other key already in
use gets an archived category. Keys are fixed, so renames relabel past
feedback. Archive hides a category from the form; only unused categories
can be deleted. Submissions are validated against active categories, and
feedback responses carry category_label.

New: GET /feedback/categories (public); /admin/feedback/categories CRUD +
reorder behind the new feedback:categories permission (granted to Admin).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Krispire Admin
2026-10-18 13:47:09 +05:30
co-authored by Claude Opus 5.5
parent fa0255e9cd
commit d76505772a
13 changed files with 610 additions and 40 deletions
+3 -2
View File
@@ -248,12 +248,13 @@ func (r *AdminRepo) ListFeedback(ctx context.Context, status string, limit, offs
status = "all"
}
rows, err := r.pg.Pool.Query(ctx, `
SELECT f.id, f.category, f.message, f.contact_email, f.created_at,
SELECT f.id, f.category, COALESCE(fc.label, f.category), f.message, f.contact_email, f.created_at,
f.acknowledged_at, f.resolved_at, f.replied_at, f.reply_message,
u.handle, u.name, u.email,
COUNT(*) OVER() AS total
FROM feedback f
LEFT JOIN users u ON u.id = f.user_id
LEFT JOIN feedback_categories fc ON fc.key = f.category
WHERE ($1 = 'all'
OR ($1 = 'open' AND f.resolved_at IS NULL)
OR ($1 = 'resolved' AND f.resolved_at IS NOT NULL))
@@ -268,7 +269,7 @@ func (r *AdminRepo) ListFeedback(ctx context.Context, status string, limit, offs
total := 0
for rows.Next() {
var f model.AdminFeedback
if err := rows.Scan(&f.ID, &f.Category, &f.Message, &f.ContactEmail, &f.CreatedAt,
if err := rows.Scan(&f.ID, &f.Category, &f.CategoryLabel, &f.Message, &f.ContactEmail, &f.CreatedAt,
&f.AcknowledgedAt, &f.ResolvedAt, &f.RepliedAt, &f.ReplyMessage,
&f.AuthorHandle, &f.AuthorName, &f.AuthorEmail, &total); err != nil {
return nil, 0, fmt.Errorf("scan admin feedback: %w", err)