feat: feature requests board with voting

- [FEATURES] tab next to PINNED in channel header
- Create, vote/unvote, filter by status (open/planned/done/rejected)
- Sort by vote count (most voted first)
- Status selector for moderation
- DB: feature_requests + feature_request_votes tables
- Backend: full CRUD + vote endpoints under /servers/{id}/feature-requests
This commit is contained in:
2026-07-02 13:11:52 -04:00
parent 6654a9ee6b
commit 0f54657b23
7 changed files with 525 additions and 1 deletions
+22
View File
@@ -534,5 +534,27 @@ CREATE TABLE IF NOT EXISTS poll_votes (
UNIQUE (option_id, user_id)
);
CREATE INDEX IF NOT EXISTS idx_poll_votes_option ON poll_votes(option_id);
-- Feature requests
CREATE TABLE IF NOT EXISTS feature_requests (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
server_id UUID NOT NULL REFERENCES servers(id) ON DELETE CASCADE,
channel_id UUID REFERENCES channels(id) ON DELETE SET NULL,
author_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
title VARCHAR(255) NOT NULL,
description TEXT NOT NULL DEFAULT '',
status VARCHAR(16) NOT NULL DEFAULT 'open' CHECK (status IN ('open', 'planned', 'done', 'rejected')),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_feature_requests_server ON feature_requests(server_id, created_at DESC);
CREATE TABLE IF NOT EXISTS feature_request_votes (
feature_request_id UUID NOT NULL REFERENCES feature_requests(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (feature_request_id, user_id)
);
CREATE INDEX IF NOT EXISTS idx_feature_request_votes_fr ON feature_request_votes(feature_request_id);
`