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:
@@ -0,0 +1,56 @@
|
||||
import { create } from "zustand";
|
||||
import api from "../lib/api.ts";
|
||||
|
||||
export interface FeatureRequest {
|
||||
id: string;
|
||||
server_id: string;
|
||||
channel_id: string;
|
||||
author_id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
status: "open" | "planned" | "done" | "rejected";
|
||||
vote_count: number;
|
||||
voters: string[];
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
interface FeatureRequestStore {
|
||||
requestsByServer: Record<string, FeatureRequest[]>;
|
||||
fetchRequests: (serverId: string) => Promise<void>;
|
||||
createRequest: (serverId: string, title: string, description: string, channelId?: string) => Promise<void>;
|
||||
vote: (serverId: string, requestId: string) => Promise<void>;
|
||||
unvote: (serverId: string, requestId: string) => Promise<void>;
|
||||
updateStatus: (serverId: string, requestId: string, status: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export const useFeatureRequestStore = create<FeatureRequestStore>((set, get) => ({
|
||||
requestsByServer: {},
|
||||
|
||||
fetchRequests: async (serverId: string) => {
|
||||
const data = await api.get<FeatureRequest[]>(`/servers/${serverId}/feature-requests`);
|
||||
set((s) => ({
|
||||
requestsByServer: { ...s.requestsByServer, [serverId]: data },
|
||||
}));
|
||||
},
|
||||
|
||||
createRequest: async (serverId, title, description, channelId) => {
|
||||
await api.post(`/servers/${serverId}/feature-requests`, { title, description, channel_id: channelId });
|
||||
await get().fetchRequests(serverId);
|
||||
},
|
||||
|
||||
vote: async (serverId, requestId) => {
|
||||
await api.post(`/servers/${serverId}/feature-requests/${requestId}/vote`);
|
||||
await get().fetchRequests(serverId);
|
||||
},
|
||||
|
||||
unvote: async (serverId, requestId) => {
|
||||
await api.delete(`/servers/${serverId}/feature-requests/${requestId}/vote`);
|
||||
await get().fetchRequests(serverId);
|
||||
},
|
||||
|
||||
updateStatus: async (serverId, requestId, status) => {
|
||||
await api.patch(`/servers/${serverId}/feature-requests/${requestId}/status`, { status });
|
||||
await get().fetchRequests(serverId);
|
||||
},
|
||||
}));
|
||||
Reference in New Issue
Block a user