import { useEffect, useMemo, useState } from 'react'; import { Link } from 'react-router-dom'; import { forumApi } from '../../utils/api'; import { timeAgo } from '../../utils/format'; import type { ForumCategory, ForumThread } from '../../types'; // ── Sub-components ───────────────────────────────────────────────────────────── function CategoryCard({ category, threads }: { category: ForumCategory; threads: ForumThread[] }) { const pinned = threads.filter((t) => t.isPinned && t.categoryId === category.id); const regular = threads.filter((t) => !t.isPinned && t.categoryId === category.id); const categoryThreads = [...pinned, ...regular]; return ( {/* Category header */} {category.icon} {category.name} {category.description} {category.threadCount} threads {/* Threads */} {categoryThreads.length === 0 ? ( No threads yet. Be the first to post. ) : ( categoryThreads.map((thread, idx) => ( {thread.isPinned && ( Pinned )} {thread.isLocked && ( Locked )} {thread.title} by {thread.authorName} {' '}— {timeAgo(thread.createdAt)} {thread.replyCount} replies )) )} ); } // ── Forum Page ───────────────────────────────────────────────────────────────── export default function ForumPage() { const [search, setSearch] = useState(''); const [categories, setCategories] = useState([]); const [threads, setThreads] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); useEffect(() => { let cancelled = false; setLoading(true); Promise.all([ forumApi.getCategories(), forumApi.getThreads({ limit: 200 }), ]) .then(([cats, threadRes]) => { if (cancelled) return; setCategories(cats); setThreads(threadRes.data); }) .catch(() => { if (cancelled) return; setError('Failed to load forum. Please try again.'); }) .finally(() => { if (!cancelled) setLoading(false); }); return () => { cancelled = true; }; }, []); const filteredCategories = useMemo(() => { if (!search.trim()) return categories; const q = search.toLowerCase(); return categories.filter((cat) => cat.name.toLowerCase().includes(q) || threads.some((t) => t.categoryId === cat.id && t.title.toLowerCase().includes(q)) ); }, [search, categories, threads]); return ( {/* Header */} Community FORUM Read freely. Login to post. {/* Search */} setSearch(e.target.value)} style={{ width: '220px' }} aria-label="Search forum threads" /> {loading && ( Loading forum... )} {error && !loading && ( {error} )} {/* Categories */} {!loading && !error && ( filteredCategories.length === 0 ? ( {search.trim() ? `No results found for "${search}"` : 'No forum categories available yet.'} ) : ( filteredCategories.map((cat) => ( )) ) )} ); }
{category.description}
Read freely. Login to post.