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
+16 -1
View File
@@ -18,7 +18,10 @@ type Feedback struct {
ID string `json:"id"`
UserID *string `json:"user_id,omitempty"`
Category string `json:"category"`
Message string `json:"message"`
// CategoryLabel is the category's current display name (falls back to
// the key if the category was ever removed).
CategoryLabel string `json:"category_label"`
Message string `json:"message"`
ContactEmail *string `json:"contact_email,omitempty"`
CreatedAt time.Time `json:"created_at"`
// AcknowledgedAt is set when an admin presses "Acknowledge" on the
@@ -50,3 +53,15 @@ type Feedback struct {
AuthorName *string `json:"author_name,omitempty"`
AuthorEmail *string `json:"-"` // server-side only; never serialised
}
// FeedbackCategory is one option on the /feedback form, managed from the
// admin panel. Key is what feedback.category stores and never changes.
type FeedbackCategory struct {
Key string `json:"key"`
Label string `json:"label"`
Description string `json:"description"`
SortOrder int `json:"sort_order"`
ArchivedAt *time.Time `json:"archived_at,omitempty"`
// UsageCount is admin-only: how much feedback is filed under it.
UsageCount int `json:"usage_count,omitempty"`
}