"use client"; import { useState, useEffect, useCallback } from "react"; import { useAuth } from "@/lib/AuthContext"; import { useRouter, useParams } from "next/navigation"; import { ArrowLeft, Crown, Star, Clock, CheckCircle, Loader2, Lock, Send, ShieldCheck, Flag, } from "lucide-react"; import ErrorFeedback from "@/components/ErrorFeedback"; interface AuthorInfo { id: string; name: string; isPremium: boolean; isPro: boolean; } interface ThreadDetail { id: string; title: string; content: string; categoryId: string; pinned: boolean; shariahStatus: "pending" | "approved" | "rejected"; shariahFlags?: string; createdAt: string; author: AuthorInfo; category: { id: string; name: string; icon?: string; }; } interface Post { id: string; content: string; threadId: string; shariahStatus: "pending" | "approved" | "rejected"; shariahFlags?: string; createdAt: string; author: AuthorInfo; } const SHARIAH_LABELS: Record = { pending: { label: "Pending Review", color: "text-amber-400", icon: Clock }, approved: { label: "Shariah Compliant", color: "text-emerald-400", icon: ShieldCheck }, rejected: { label: "Flagged", color: "text-red-400", icon: Flag }, }; export default function ThreadDetailPage() { const { user, token, loading } = useAuth(); const router = useRouter(); const params = useParams(); const threadId = params.threadId as string; const [thread, setThread] = useState(null); const [posts, setPosts] = useState([]); const [loadingData, setLoadingData] = useState(true); const [error, setError] = useState(null); // Reply form const [replyContent, setReplyContent] = useState(""); const [submitting, setSubmitting] = useState(false); const [replyError, setReplyError] = useState(null); const canPost = user?.isPremium || user?.isPro; const fetchThread = useCallback(async () => { try { const res = await fetch(`/mobile/api/forum/threads`); const data = await res.json(); if (res.ok) { const found = data.threads.find((t: ThreadDetail) => t.id === threadId); if (found) { setThread(found); } else { // Try fetching thread detail individually (use threads endpoint with the thread) // Since we don't have a single-thread endpoint, we'll get it from the list } } } catch { // ignore } }, [threadId]); const fetchPosts = useCallback(async () => { setLoadingData(true); setError(null); try { const res = await fetch(`/mobile/api/forum/posts?threadId=${threadId}`); const data = await res.json(); if (res.ok) { setPosts(data.posts); } else { setError(data.error || "Failed to load posts"); } } catch { setError("Network error. Please try again."); } finally { setLoadingData(false); } }, [threadId]); useEffect(() => { if (!loading && !token) { router.push("/auth"); return; } }, [loading, token, router]); useEffect(() => { if (token && threadId) { fetchThread(); fetchPosts(); } }, [token, threadId, fetchThread, fetchPosts]); const handleReply = async (e: React.FormEvent) => { e.preventDefault(); if (!replyContent.trim()) return; setReplyError(null); setSubmitting(true); try { const res = await fetch("/mobile/api/forum/posts", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, body: JSON.stringify({ content: replyContent.trim(), threadId, }), }); const data = await res.json(); if (res.ok) { setReplyContent(""); fetchPosts(); } else { setReplyError(data.error || "Failed to post reply"); } } catch { setReplyError("Network error. Please try again."); } finally { setSubmitting(false); } }; function timeAgo(dateStr: string): string { const now = Date.now(); const then = new Date(dateStr).getTime(); const diffSec = Math.floor((now - then) / 1000); if (diffSec < 60) return "just now"; const diffMin = Math.floor(diffSec / 60); if (diffMin < 60) return `${diffMin}m ago`; const diffHr = Math.floor(diffMin / 60); if (diffHr < 24) return `${diffHr}h ago`; const diffDay = Math.floor(diffHr / 24); if (diffDay < 7) return `${diffDay}d ago`; return new Date(dateStr).toLocaleDateString(); } if (loading) { return (
); } if (!user) return null; return (
{/* Header */}

{thread ? thread.title : "Thread"}

{loadingData ? (

Loading thread...

) : error ? ( { fetchThread(); fetchPosts(); }} context="thread posts" /> ) : !thread ? (

Thread not found

) : ( <> {/* Original thread post */}
{/* Author + category */}
{thread.author.name.charAt(0).toUpperCase()}
{thread.author.name} {thread.author.isPro && ( )} {thread.author.isPremium && !thread.author.isPro && ( )}
{thread.category.name} · {timeAgo(thread.createdAt)} {/* Shariah compliance */} ·
{(() => { const Icon = SHARIAH_LABELS[thread.shariahStatus].icon; return ; })()} {SHARIAH_LABELS[thread.shariahStatus].label}
{/* Title & content */}

{thread.title}

{thread.content}

{/* Posts list */}
{posts.length > 0 && (
{posts.length} {posts.length === 1 ? "Reply" : "Replies"}
)} {posts.map((post) => { const shariahInfo = SHARIAH_LABELS[post.shariahStatus]; const ShariahIcon = shariahInfo.icon; return (
{/* Author info */}
{post.author.name.charAt(0).toUpperCase()}
{post.author.name} {post.author.isPro && ( )} {post.author.isPremium && !post.author.isPro && ( )}
{timeAgo(post.createdAt)}
{/* Content */}

{post.content}

{/* Shariah compliance label */}
{shariahInfo.label}
); })}
{/* Reply form — premium gated for free users */}
{!canPost ? (

Premium Feature

Upgrade to Premium or Pro to join the discussion and reply to threads.

) : (