From dc166dfb109e9c55216a601951b71d0fc46b5a51 Mon Sep 17 00:00:00 2001 From: Ryan Salazar Date: Wed, 20 May 2026 08:34:05 +0000 Subject: [PATCH] forum: fix API + UI column-name mismatch against bb.forum_threads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The schema has upvotes / downvotes / vote_score; API + UI were querying/reading upvote_count / downvote_count (non-existent columns). Threads existed in the DB but didn't render. - /api/forum/threads route.ts: - 'top-voted' sort: order by vote_score (was upvote_count) - 'newest' default sort: forum_threads has no 'status' column, so order pinned-first via is_pinned, then last_reply_at desc - UI ForumThread interface + components: rename upvote_count → upvotes, add downvotes + vote_score so the vote display in the thread cards and the admin moderation list pulls live data from the existing columns. After this lands and the deploy completes, the 50 seeded threads + the 9 ai-seeded new threads + 253 backfilled replies already in the DB will all render at /forum. --- src/app/admin/forum-moderation/page.tsx | 2 +- src/app/api/forum/threads/route.ts | 9 ++++++--- src/app/forum/[slug]/page.tsx | 6 +++--- src/app/forum/page.tsx | 9 ++++++--- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/app/admin/forum-moderation/page.tsx b/src/app/admin/forum-moderation/page.tsx index a5e2f42..9eab546 100644 --- a/src/app/admin/forum-moderation/page.tsx +++ b/src/app/admin/forum-moderation/page.tsx @@ -14,7 +14,7 @@ interface ForumThread { body: string; reply_count: number; view_count: number; - upvote_count: number; + upvotes: number; is_pinned: boolean; is_featured: boolean; is_locked: boolean; diff --git a/src/app/api/forum/threads/route.ts b/src/app/api/forum/threads/route.ts index 2801883..6f06733 100644 --- a/src/app/api/forum/threads/route.ts +++ b/src/app/api/forum/threads/route.ts @@ -31,7 +31,9 @@ export async function GET(req: NextRequest) { } if (sort === 'top-voted') { - query = query.order('upvote_count', { ascending: false }).order('created_at', { ascending: false }); + // The schema has upvotes / downvotes / vote_score (no upvote_count). + // Use vote_score so downvotes properly demote a thread. + query = query.order('vote_score', { ascending: false }).order('created_at', { ascending: false }); } else if (sort === 'most-viewed') { query = query.order('view_count', { ascending: false }).order('created_at', { ascending: false }); } else if (sort === 'most-replied') { @@ -39,8 +41,9 @@ export async function GET(req: NextRequest) { } else if (sort === 'unanswered') { query = query.eq('reply_count', 0).order('created_at', { ascending: false }); } else { - // newest — default - query = query.order('status', { ascending: false }).order('last_reply_at', { ascending: false }); + // newest — default. forum_threads has no 'status' column; sort + // pinned-first then by recent activity. + query = query.order('is_pinned', { ascending: false }).order('last_reply_at', { ascending: false, nullsFirst: false }); } const { data, error, count } = await query; diff --git a/src/app/forum/[slug]/page.tsx b/src/app/forum/[slug]/page.tsx index 78e952b..0c5bc52 100644 --- a/src/app/forum/[slug]/page.tsx +++ b/src/app/forum/[slug]/page.tsx @@ -22,7 +22,7 @@ interface ForumThread { status: string; reply_count: number; view_count: number; - upvote_count?: number; + upvotes?: number; last_reply_at: string; created_at: string; is_ai_seeded: boolean; @@ -71,7 +71,7 @@ function TopThreadsWidget({ categoryId }: { categoryId: string }) { ]; const getMetricValue = (thread: ForumThread) => { - if (activeMetric === 'upvotes') return { value: thread.upvote_count ?? 0, label: 'votes', color: 'text-[#22c55e]' }; + if (activeMetric === 'upvotes') return { value: thread.upvotes ?? 0, label: 'votes', color: 'text-[#22c55e]' }; if (activeMetric === 'replies') return { value: thread.reply_count, label: 'replies', color: 'text-[#3b82f6]' }; return { value: thread.view_count, label: 'views', color: 'text-[#f59e0b]' }; }; @@ -451,7 +451,7 @@ export default function ForumCategoryPage() { {sort === 'top-voted' && (
-
+{thread.upvote_count ?? 0}
+
+{thread.upvotes ?? 0}
votes
)} diff --git a/src/app/forum/page.tsx b/src/app/forum/page.tsx index 9f0ccbd..2a49009 100644 --- a/src/app/forum/page.tsx +++ b/src/app/forum/page.tsx @@ -22,12 +22,15 @@ interface ForumCategory { interface ForumThread { id: string; title: string; + author_id?: string | null; author_name: string; reply_count: number; view_count: number; last_reply_at: string; created_at: string; - upvote_count?: number; + upvotes?: number; + downvotes?: number; + vote_score?: number; forum_categories?: { name: string; slug: string }; } @@ -77,7 +80,7 @@ function TopThreadsWidget({ categoryId }: { categoryId?: string }) { ]; const getMetricValue = (thread: ForumThread) => { - if (activeMetric === 'upvotes') return { value: thread.upvote_count ?? 0, label: 'votes', color: 'text-[#22c55e]' }; + if (activeMetric === 'upvotes') return { value: thread.upvotes ?? 0, label: 'votes', color: 'text-[#22c55e]' }; if (activeMetric === 'replies') return { value: thread.reply_count, label: 'replies', color: 'text-[#3b82f6]' }; return { value: thread.view_count, label: 'views', color: 'text-[#f59e0b]' }; }; @@ -493,7 +496,7 @@ export default function ForumIndexPage() { {sort === 'top-voted' && (
-
+{thread.upvote_count ?? 0}
+
+{thread.upvotes ?? 0}
votes
)}