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:
co-authored by
Claude Opus 5.5
parent
fa0255e9cd
commit
d76505772a
@@ -0,0 +1,2 @@
|
||||
UPDATE admin_roles SET permissions = array_remove(permissions, 'feedback:categories');
|
||||
DROP TABLE IF EXISTS feedback_categories;
|
||||
@@ -0,0 +1,41 @@
|
||||
-- Feedback categories, managed from the admin panel instead of hard-coded in
|
||||
-- the app and the BFF.
|
||||
--
|
||||
-- `key` is what feedback.category stores, and never changes once created —
|
||||
-- so renaming a category in the admin relabels every past submission too.
|
||||
-- Archiving hides a category from the /feedback form while existing feedback
|
||||
-- keeps its label. There is deliberately no foreign key from feedback: the
|
||||
-- column predates this table, and the admin only hard-deletes categories
|
||||
-- nothing uses.
|
||||
CREATE TABLE IF NOT EXISTS feedback_categories (
|
||||
key TEXT PRIMARY KEY,
|
||||
label TEXT NOT NULL,
|
||||
description TEXT NOT NULL DEFAULT '',
|
||||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||||
archived_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
-- The four categories the app offered until now, with the exact labels and
|
||||
-- descriptions its form showed, in the form's order. Production feedback
|
||||
-- already uses these keys, so they must stay byte-for-byte the same.
|
||||
INSERT INTO feedback_categories (key, label, description, sort_order) VALUES
|
||||
('general', 'General feedback', 'Thoughts, impressions, suggestions — anything else.', 0),
|
||||
('bug', 'Bug report', 'Something is broken or behaving unexpectedly.', 1),
|
||||
('feature', 'Feature request', 'An idea for something new on the site.', 2),
|
||||
('other', 'Other', 'Doesn''t fit the above? Use this.', 3)
|
||||
ON CONFLICT (key) DO NOTHING;
|
||||
|
||||
-- Safety net: any other key already present in feedback (from an older
|
||||
-- client) gets a category too — archived, so it isn't offered on the form,
|
||||
-- but its submissions still carry a readable label.
|
||||
INSERT INTO feedback_categories (key, label, sort_order, archived_at)
|
||||
SELECT DISTINCT f.category, initcap(replace(f.category, '_', ' ')), 100, now()
|
||||
FROM feedback f
|
||||
WHERE NOT EXISTS (SELECT 1 FROM feedback_categories c WHERE c.key = f.category)
|
||||
ON CONFLICT (key) DO NOTHING;
|
||||
|
||||
-- The Admin role may manage them; Superadmin already has '*'.
|
||||
UPDATE admin_roles
|
||||
SET permissions = array_append(permissions, 'feedback:categories')
|
||||
WHERE key = 'admin' AND NOT ('feedback:categories' = ANY(permissions));
|
||||
Reference in New Issue
Block a user