T.ME/BIBIL_0DAY
CasperSecurity


Server : Apache/2
System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64
User : gositeme ( 1004)
PHP Version : 8.2.29
Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname
Directory :  /home/gositeme/domains/lavocat.ca/public_html/src/components/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/gositeme/domains/lavocat.ca/public_html/src/components/EnhancedComments.tsx
import React, { useState, useEffect, useRef, useCallback } from 'react';
import { useSession } from 'next-auth/react';
import { motion, AnimatePresence } from 'framer-motion';
import { toast } from 'react-hot-toast';
import { format, formatDistanceToNow } from 'date-fns';
import ReactionPicker from './ReactionPicker';
import DOMPurify from 'dompurify';
import Link from 'next/link';
import RequireAuth from './RequireAuth';
import {
  ThumbsUp,
  ThumbsDown,
  Heart,
  Smiley,
  SmileySad,
  Fire,
  Star,
  Confetti,
  Warning,
  Info,
  ChatCircle,
  CheckCircle,
  MagnifyingGlassPlus,
  Paperclip,
  ChatsCircle,
  PencilSimple,
  Trash,
  Flag,
  UserCircle,
  XCircle,
  PaperPlaneRight,
  Question,
  Trophy,
  CurrencyDollar,
  Scales,
  Handshake,
  Shield,
  ShieldCheck,
  Medal,
  Crown,
  Sword,
  Target,
  CheckCircle as CheckCircleIcon,
  FileText,
  Scroll,
  Buildings,
  GraduationCap
} from 'phosphor-react';
import { Gavel } from 'lucide-react';

interface CommentUser {
  id: string;
  name: string;
  username?: string;
  profilePicture?: string;
  role: string;
  isVerified?: boolean;
  reputation?: number;
}

interface CommentReaction {
  id: string;
  reactionType: string;
  user: {
    id: string;
    name: string;
    username?: string;
  };
}

interface Comment {
  id: string;
  content: string;
  createdAt: string;
  updatedAt: string;
  likes: number;
  isEdited: boolean;
  isDeleted: boolean;
  isPinned?: boolean;
  isHighlighted?: boolean;
  user: CommentUser;
  replies: Comment[];
  reactions?: CommentReaction[];
  attachments?: Array<{
    id: string;
    name: string;
    url: string;
    type: string;
    size: number;
    file?: File;
  }>;
  _count: {
    replies: number;
    likedBy: number;
    reactions: number;
  };
}

interface EnhancedCommentsProps {
  caseId: string;
  initialComments?: Comment[];
  onCommentAdded?: () => void;
  mode?: 'public' | 'private' | 'admin';
  allowAttachments?: boolean;
  allowReactions?: boolean;
  allowReplies?: boolean;
  maxRepliesDepth?: number;
  apiEndpoint?: string;
}

const EnhancedComments: React.FC<EnhancedCommentsProps> = ({
  caseId,
  initialComments = [],
  onCommentAdded,
  mode = 'public',
  allowAttachments = true,
  allowReactions = true,
  allowReplies = true,
  maxRepliesDepth = 3,
  apiEndpoint
}) => {
  const { data: session } = useSession();
  const [comments, setComments] = useState<Comment[]>(initialComments);
  const [loading, setLoading] = useState(false);
  const [posting, setPosting] = useState(false);
  const [newComment, setNewComment] = useState('');
  const [replyingTo, setReplyingTo] = useState<string | null>(null);
  const [replyContent, setReplyContent] = useState<{ [commentId: string]: string }>({});
  const [editingComment, setEditingComment] = useState<string | null>(null);
  const [editContent, setEditContent] = useState('');
  const [showReplies, setShowReplies] = useState<Set<string>>(new Set());
  const [likedComments, setLikedComments] = useState<Set<string>>(new Set());
  const [sortBy, setSortBy] = useState<'newest' | 'oldest' | 'mostLiked' | 'mostReplies'>('newest');
  const [filterBy, setFilterBy] = useState<'all' | 'lawyers' | 'clients' | 'admins'>('all');
  const [searchTerm, setSearchTerm] = useState('');
  const [showAdvancedOptions, setShowAdvancedOptions] = useState(false);
  const [attachments, setAttachments] = useState<{ id: string; name: string; url: string; type: string; size: number; file?: File }[]>([]);
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [page, setPage] = useState(1);
  const [hasMore, setHasMore] = useState(true);
  const [loadingMore, setLoadingMore] = useState(false);
  const [selectedImage, setSelectedImage] = useState<{ url: string; name: string } | null>(null);
  const [showImageModal, setShowImageModal] = useState(false);
  const limit = 20;
  
  const textareaRef = useRef<HTMLTextAreaElement>(null);
  const fileInputRef = useRef<HTMLInputElement>(null);
  const replyInputRefs = useRef<{ [key: string]: HTMLInputElement | null }>({});

  const getApiEndpoint = () => apiEndpoint || `/api/live-cases/${caseId}/comments`;

  // Utility function to normalize comment data
  const normalizeComment = (comment: any): Comment => ({
    ...comment,
    reactions: comment.reactions ?? [],
    attachments: comment.attachments ?? [],
    replies: (comment.replies ?? []).map((reply: any) => ({
      ...reply,
      reactions: reply.reactions ?? [],
      attachments: reply.attachments ?? [],
      _count: {
        replies: reply._count?.replies || reply.replies?.length || 0,
        likedBy: reply._count?.likedBy || 0,
        reactions: reply._count?.reactions || reply.reactions?.length || 0
      }
    })),
    _count: {
      replies: comment._count?.replies || comment.replies?.length || 0,
      likedBy: comment._count?.likedBy || 0,
      reactions: comment._count?.reactions || comment.reactions?.length || 0
    }
  });

  useEffect(() => {
    fetchComments(true);
  }, [caseId, sortBy, filterBy, searchTerm]);

  const fetchComments = async (reset = false) => {
    try {
      if (reset) {
        setPage(1);
        setComments([]);
        setHasMore(true);
      }
      setLoading(true);
      const params = new URLSearchParams({
        page: reset ? '1' : String(page),
        limit: String(limit),
        sortBy,
        filterBy,
        search: searchTerm
      });
      const response = await fetch(`${getApiEndpoint()}?${params}`);
      if (response.ok) {
        const data = await response.json();
        const newComments = data.comments.map(normalizeComment);
        if (reset) {
          setComments(prev => dedupeComments([...newComments, ...prev]));
        } else {
          setComments(prev => dedupeComments([...newComments, ...prev]));
        }
        setHasMore(newComments.length === limit);
      } else {
        console.error('Failed to fetch comments:', response.status);
        if (reset) {
          setComments([]);
        }
      }
    } catch (error) {
      console.error('Error fetching comments:', error);
      if (reset) {
        setComments([]);
      }
    } finally {
      setLoading(false);
      setLoadingMore(false);
    }
  };

  const handleAddComment = async () => {
    if (!newComment.trim() || isSubmitting) return;
    setIsSubmitting(true);
    try {
      let res;
      
      if (attachments.length > 0) {
        // Use multipart/form-data for comments with attachments
        const formData = new FormData();
        formData.append('content', newComment);
        attachments.forEach(attachment => {
          // Convert attachment back to file if possible, or skip
          if (attachment.file) {
            formData.append('attachments', attachment.file);
          }
        });
        
        res = await fetch(getApiEndpoint(), {
          method: 'POST',
          body: formData,
        });
      } else {
        // Use JSON for comments without attachments
        res = await fetch(getApiEndpoint(), {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            content: newComment,
          }),
        });
      }
      
      if (!res.ok) throw new Error('Failed to post comment');
      const data = await res.json();
      setComments(prev => dedupeComments([normalizeComment(data.comment), ...prev]));
      setNewComment('');
      // Clean up object URLs before clearing attachments
      attachments.forEach(attachment => {
        if (attachment.url && attachment.url.startsWith('blob:')) {
          URL.revokeObjectURL(attachment.url);
        }
      });
      setAttachments([]);
      if (onCommentAdded) onCommentAdded();
      fetchComments(); // background refresh for consistency
      toast.success('Comment posted!');
    } catch (err) {
      toast.error('Failed to post comment');
    } finally {
      setIsSubmitting(false);
    }
  };

  // Add this helper before handleAddReply
  function addReplyById(comments: Comment[], parentId: string, reply: Comment): Comment[] {
    return comments.map(comment => {
      if (comment.id === parentId) {
        return {
          ...comment,
          replies: [...comment.replies, reply],
          _count: {
            ...comment._count,
            replies: comment._count.replies + 1
          }
        };
      }
      return {
        ...comment,
        replies: addReplyById(comment.replies, parentId, reply)
      };
    });
  }

  // Update handleAddReply to use the helper
  const handleAddReply = useCallback(async (parentId: string) => {
    if (!session) {
      toast.error('Please login to reply');
      return;
    }
    if (!replyContent[parentId] || !replyContent[parentId].trim()) {
      toast.error('Please enter a reply');
      return;
    }
    try {
      const response = await fetch(getApiEndpoint(), {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ 
          content: replyContent[parentId].trim(),
          parentId: parentId
        })
      });
      if (response.ok) {
        const data = await response.json();
        setComments(prev => addReplyById(prev, parentId, normalizeComment(data.comment)));
        setReplyContent(prev => ({ ...prev, [parentId]: '' }));
        setReplyingTo(null);
        toast.success('Reply added successfully! 💬');
      } else {
        toast.error('Failed to add reply');
      }
    } catch (error) {
      console.error('Error adding reply:', error);
      toast.error('Error adding reply');
    }
  }, [session, replyContent, caseId]);

  // Helper: Check if current user has reacted to a comment with a given type
  const userHasReacted = (comment: Comment, reactionType: string) => {
    const userId = session?.user?.id;
    return (comment.reactions || []).some(r => r.reactionType === reactionType && r.user.id === userId);
  };

  // Helper: Get count for a reaction type
  const getReactionCount = (comment: Comment, reactionType: string) => {
    return (comment.reactions || []).filter(r => r.reactionType === reactionType).length;
  };

  // Robust, modern reaction handler
  const handleReactionClick = useCallback(async (commentId: string, reactionType: string) => {
    if (!session) {
      toast.error('Please login to react to comments');
      return;
    }
    const userId = session.user.id;
    let optimisticAdd = true;

    setComments(prevComments => prevComments.map(comment => {
      if (comment.id !== commentId) return comment;
      const reactionsArr = comment.reactions || [];
      const alreadyReacted = reactionsArr.some(r => r.reactionType === reactionType && r.user.id === userId);
      let newReactions;
      if (alreadyReacted) {
        // Remove user's reaction (toggle off)
        newReactions = reactionsArr.filter(r => !(r.reactionType === reactionType && r.user.id === userId));
        optimisticAdd = false;
      } else {
        // Add user's reaction (toggle on)
        newReactions = [
          ...reactionsArr,
          {
            id: `temp-${Date.now()}`,
            reactionType,
            user: { id: userId, name: session.user.name || '' }
          }
        ];
      }
      return { ...comment, reactions: newReactions };
    }));

    try {
      const response = await fetch(`${getApiEndpoint()}/${commentId}/reactions`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ reactionType }),
      });
      if (!response.ok) throw new Error('Failed to update reaction');
      // Optionally, update only the affected comment with the server response if available
      // (Assume API returns updated comment)
      // const data = await response.json();
      // setComments(prev => prev.map(c => c.id === commentId ? normalizeComment(data.comment) : c));
    } catch (error) {
      // Revert only the affected comment's reactions
      setComments(prevComments => prevComments.map(comment => {
        if (comment.id !== commentId) return comment;
        const reactionsArr = comment.reactions || [];
        if (optimisticAdd) {
          // Remove the temp reaction
          return { ...comment, reactions: reactionsArr.filter(r => !(r.reactionType === reactionType && r.user.id === userId)) };
        } else {
          // Add back the reaction
          return { ...comment, reactions: [
            ...reactionsArr,
            {
              id: `temp-${Date.now()}`,
              reactionType,
              user: { id: userId, name: session.user.name || '' }
            }
          ] };
        }
      }));
      toast.error('Failed to update reaction');
    }
  }, [session, getApiEndpoint, setComments]);

  const updateCommentReactions = (comment: Comment, reactionType: string, isAdding: boolean): Comment => {
    const existingReaction = comment.reactions?.find(r => 
      r.reactionType === reactionType && r.user.id === session?.user?.id
    );

    if (isAdding && !existingReaction) {
      return {
        ...comment,
        reactions: [
          ...(comment.reactions || []),
          {
            id: `temp-${Date.now()}`,
            reactionType,
            user: { id: session?.user?.id || '', name: session?.user?.name || '' }
          }
        ]
      };
    } else if (!isAdding && existingReaction) {
      return {
        ...comment,
        reactions: comment.reactions?.filter(r => r.id !== existingReaction.id) || []
      };
    }

    return comment;
  };

  const getReactionData = (comment: Comment) => {
    const reactionCounts: { [key: string]: number } = {};
    const userReactions: { [key: string]: boolean } = {};

    (comment.reactions || []).forEach(reaction => {
      reactionCounts[reaction.reactionType] = (reactionCounts[reaction.reactionType] || 0) + 1;
      if (reaction.user.id === session?.user?.id) {
        userReactions[reaction.reactionType] = true;
      }
    });

    return Object.keys(reactionCounts).map(type => ({
      reactionType: type,
      count: reactionCounts[type],
      userReacted: userReactions[type] || false
    }));
  };

  const toggleReplies = (commentId: string) => {
    setShowReplies(prev => {
      const newSet = new Set(prev);
      if (newSet.has(commentId)) {
        newSet.delete(commentId);
      } else {
        newSet.add(commentId);
      }
      return newSet;
    });
  };

  const handleFileUpload = async (event: React.ChangeEvent<HTMLInputElement>) => {
    const files = Array.from(event.target.files || []);
    const maxSize = 5 * 1024 * 1024; // 5MB
    const allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'application/pdf', 'text/plain'];

    for (const file of files) {
      if (file.size > maxSize) {
        toast.error(`${file.name} is too large. Maximum size is 5MB.`);
        continue;
      }
      if (!allowedTypes.includes(file.type)) {
        toast.error(`${file.name} is not a supported file type.`);
        continue;
      }
      
      // Store the file directly for later upload with the comment
      setAttachments(prev => [
        ...prev,
        {
          id: Date.now().toString() + Math.random().toString(36).substr(2, 9),
          name: file.name,
          url: URL.createObjectURL(file), // Create preview URL
          type: file.type,
          size: file.size,
          file: file, // Store the actual file
        },
      ]);
      toast.success(`${file.name} added!`);
    }
  };

  const removeAttachment = (index: number) => {
    setAttachments(prev => {
      const newAttachments = prev.filter((_, i) => i !== index);
      // Clean up the object URL for the removed attachment
      if (prev[index]?.url && prev[index].url.startsWith('blob:')) {
        URL.revokeObjectURL(prev[index].url);
      }
      return newAttachments;
    });
  };

  const filteredComments = comments.filter(comment => {
    if (filterBy === 'all') return true;
    return comment.user.role.toUpperCase() === filterBy.toUpperCase();
  });

  const sortedComments = [...filteredComments].sort((a, b) => {
    switch (sortBy) {
      case 'oldest':
        return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
      case 'mostLiked':
        return b.likes - a.likes;
      case 'mostReplies':
        return b._count.replies - a._count.replies;
      default: // newest
        return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
    }
  });

  // Modern comment card component
  const CommentCard: React.FC<{
    comment: Comment;
    depth: number;
    onReply: (id: string) => void;
    onEdit: (id: string) => void;
    onDelete: (id: string) => void;
    onReact: (id: string, reaction: string) => void;
    isReplying: boolean;
    isEditing: boolean;
    parentId?: string;
  }> = ({ comment, depth, onReply, onEdit, onDelete, onReact, isReplying, isEditing, parentId }) => {
    const roleColors: Record<string, string> = {
      SUPERADMIN: 'bg-gradient-to-r from-purple-500 to-pink-500 text-white',
      ADMIN: 'bg-gradient-to-r from-blue-500 to-blue-700 text-white',
      LAWYER: 'bg-gradient-to-r from-green-500 to-green-700 text-white',
      CLIENT: 'bg-gradient-to-r from-yellow-400 to-yellow-600 text-white',
      USER: 'bg-gray-200 text-gray-800',
    };
    const avatar = comment.user.profilePicture
      ? <img src={comment.user.profilePicture} alt={comment.user.name} className="h-10 w-10 rounded-full object-cover border-2 border-white shadow" />
      : <div className={`h-10 w-10 rounded-full flex items-center justify-center font-bold text-lg ${roleColors[comment.user.role] || 'bg-gray-200 text-gray-800'}`}>{comment.user.name?.[0] || '?'}</div>;
    return (
      <div className={`relative group bg-white rounded-xl shadow-md p-4 mb-4 ${depth > 0 ? 'ml-8 border-l-4 border-blue-100' : ''}`}
        tabIndex={0} aria-label={`Comment by ${comment.user.name}`}
      >
        <div className="flex items-start gap-3">
          {avatar}
          <div className="flex-1">
            <div className="flex items-center gap-2 mb-1">
              <span className="font-semibold text-gray-900">{comment.user.name}</span>
              <span className={`text-xs px-2 py-1 rounded-full ml-1 ${roleColors[comment.user.role] || 'bg-gray-200 text-gray-800'}`}>{comment.user.role}</span>
              {comment.user.isVerified && <CheckCircle className="h-4 w-4 text-blue-500 ml-1" />}
              <span className="text-xs text-gray-500 ml-2">{formatDistanceToNow(new Date(comment.createdAt), { addSuffix: true })}</span>
              {comment.isEdited && <span className="text-xs text-gray-400 ml-1">(edited)</span>}
              {comment.isPinned && <Star className="h-4 w-4 text-yellow-400 ml-1" />}
            </div>
            {isEditing ? (
              <div className="mt-3 p-4 bg-gray-50 rounded-lg border border-gray-200">
                <div className="flex items-start gap-3">
                  <div className="flex-shrink-0">
                    <div className="w-8 h-8 bg-gradient-to-br from-blue-500 to-purple-600 rounded-full flex items-center justify-center text-white font-semibold text-sm">
                      {session?.user?.image ? (
                        <img 
                          src={session.user.image} 
                          alt={session.user.name || 'User'}
                          className="w-8 h-8 rounded-full object-cover"
                        />
                      ) : (
                        <UserCircle className="h-4 w-4" />
                      )}
                    </div>
                  </div>
                  <div className="flex-1 space-y-2">
                    <textarea
                      className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent text-sm"
                      rows={3}
                      value={editContent}
                      onChange={e => setEditContent(e.target.value)}
                      aria-label="Edit comment input"
                      placeholder="Edit your comment..."
                    />
                    <div className="flex gap-2">
                      <button
                        type="button"
                        className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 text-sm font-medium transition-colors"
                        onClick={async () => {
                          if (!editContent.trim()) return;
                          setIsSubmitting(true);
                          try {
                            const res = await fetch(`${getApiEndpoint()}/${comment.id}`, {
                              method: 'PATCH',
                              headers: { 'Content-Type': 'application/json' },
                              body: JSON.stringify({ content: editContent, attachments: comment.attachments }),
                            });
                            if (!res.ok) throw new Error('Failed to edit comment');
                            setEditingComment(null);
                            setEditContent('');
                            await fetchComments();
                            toast.success('Comment updated!');
                          } catch (err) {
                            toast.error('Failed to update comment');
                          } finally {
                            setIsSubmitting(false);
                          }
                        }}
                        disabled={isSubmitting || !editContent.trim()}
                        aria-label="Save edited comment"
                      >
                        {isSubmitting ? 'Saving...' : 'Save Changes'}
                      </button>
                      <button
                        type="button"
                        className="px-4 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 text-sm font-medium transition-colors"
                        onClick={() => { setEditingComment(null); setEditContent(''); }}
                        disabled={isSubmitting}
                        aria-label="Cancel editing"
                      >
                        Cancel
                      </button>
                    </div>
                  </div>
                </div>
              </div>
            ) : (
              <div className="prose prose-sm max-w-none text-gray-800 mb-2" dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(comment.content) }} />
            )}
            {comment.attachments && comment.attachments.length > 0 && (
              <div className="flex flex-wrap gap-2 mb-2">
                <div className="w-full text-xs text-gray-500 mb-1">📎 Attachments ({comment.attachments.length}):</div>
                {comment.attachments.map(att => {
                  const isImage = att.type.startsWith('image/');
                  const isPDF = att.type === 'application/pdf';
                  const isWord = att.type === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' || att.type === 'application/msword';
                  return (
                    <div key={att.id} className="flex items-center gap-2 p-2 bg-gray-50 rounded-lg border">
                      {isImage ? (
                        <div className="flex flex-col items-center">
                          <div 
                            className="relative cursor-pointer group"
                            onClick={() => {
                              setSelectedImage({ url: att.url, name: att.name });
                              setShowImageModal(true);
                            }}
                          >
                            <img 
                              src={att.url} 
                              alt={att.name} 
                              className="h-24 w-24 object-cover rounded border-2 border-gray-200 shadow-sm transition-transform group-hover:scale-105" 
                              onError={(e) => {
                                console.error('Image failed to load:', att.url);
                                e.currentTarget.style.display = 'none';
                              }}
                            />
                            <div className="absolute inset-0 bg-black bg-opacity-0 group-hover:bg-opacity-20 transition-all duration-200 rounded flex items-center justify-center">
                              <MagnifyingGlassPlus className="h-6 w-6 text-white opacity-0 group-hover:opacity-100 transition-opacity duration-200" />
                            </div>
                          </div>
                          <span className="text-xs text-gray-600 mt-1">{att.name}</span>
                        </div>
                      ) : isPDF ? (
                        <div className="flex items-center gap-2">
                          <svg width="32" height="32" fill="none" viewBox="0 0 24 24">
                            <rect width="24" height="24" rx="4" fill="#E53E3E"/>
                            <text x="12" y="16" textAnchor="middle" fill="#fff" fontSize="12" fontWeight="bold">PDF</text>
                          </svg>
                          <div className="flex flex-col">
                            <span className="text-sm font-medium text-gray-700">{att.name}</span>
                            <span className="text-xs text-gray-500">({(att.size / 1024 / 1024).toFixed(2)} MB)</span>
                          </div>
                        </div>
                      ) : isWord ? (
                        <div className="flex items-center gap-2">
                          <svg width="32" height="32" fill="none" viewBox="0 0 24 24">
                            <rect width="24" height="24" rx="4" fill="#3182CE"/>
                            <text x="12" y="16" textAnchor="middle" fill="#fff" fontSize="12" fontWeight="bold">DOC</text>
                          </svg>
                          <div className="flex flex-col">
                            <span className="text-sm font-medium text-gray-700">{att.name}</span>
                            <span className="text-xs text-gray-500">({(att.size / 1024 / 1024).toFixed(2)} MB)</span>
                          </div>
                        </div>
                      ) : (
                        <div className="flex items-center gap-2">
                          <Paperclip className="h-6 w-6 text-gray-500" />
                          <div className="flex flex-col">
                            <span className="text-sm font-medium text-gray-700">{att.name}</span>
                            <span className="text-xs text-gray-500">({(att.size / 1024 / 1024).toFixed(2)} MB)</span>
                          </div>
                        </div>
                      )}
                    </div>
                  );
                })}
              </div>
            )}
            <div className="flex items-center gap-2 text-xs text-gray-500 mt-1">
              {/* Show top reactions inline, rest in popover */}
              {EMOJI_REACTIONS.slice(0, 3).map(r => {
                const count = getReactionCount(comment, r.type);
                const users = (comment.reactions || []).filter(rx => rx.reactionType === r.type).map(rx => rx.user);
                return (
                  <span
                    key={r.type}
                    style={{ display: 'inline-flex', alignItems: 'center', gap: 4, marginRight: 8 }}
                  >
                    <button
                      type="button"
                      aria-label={r.label}
                      onClick={() => onReact(comment.id, r.type)}
                      className={`focus:outline-none transition-colors ${userHasReacted(comment, r.type) ? 'font-bold' : ''}`}
                      style={{ textDecoration: 'none' }}
                    >
                      <span style={{ fontSize: 22, filter: userHasReacted(comment, r.type) ? 'drop-shadow(0 0 2px ' + r.color + ')' : 'none' }}>{r.emoji}</span>
                    </button>
                    {count > 0 && <ReactionUserListPopover users={users} />}
                  </span>
                );
              })}
              {/* Show top 2 justice icons */}
              {ICON_REACTIONS.slice(0, 2).map(r => {
                const Icon = r.icon;
                const count = getReactionCount(comment, r.type);
                const users = (comment.reactions || []).filter(rx => rx.reactionType === r.type).map(rx => rx.user);
                return Icon ? (
                  <span
                    key={r.type}
                    style={{ display: 'inline-flex', alignItems: 'center', gap: 4, marginRight: 8 }}
                  >
                    <button
                      type="button"
                      aria-label={r.label}
                      onClick={() => onReact(comment.id, r.type)}
                      className={`focus:outline-none transition-colors ${userHasReacted(comment, r.type) ? 'font-bold' : ''}`}
                      style={{ textDecoration: 'none' }}
                    >
                      <Icon size={22} color={r.color} weight={userHasReacted(comment, r.type) ? 'fill' : 'regular'} />
                    </button>
                    {count > 0 && <ReactionUserListPopover users={users} />}
                  </span>
                ) : null;
              })}
              {/* Popover for all reactions */}
              <ReactionPopover onReact={onReact} comment={comment} />
            </div>
            
            {/* Comment Actions */}
            <div className="flex items-center gap-2 mt-3 pt-2 border-t border-gray-100">
              <button
                type="button"
                onClick={() => onReply(comment.id)}
                className="flex items-center gap-1 text-xs text-gray-500 hover:text-blue-600 transition-colors"
                disabled={isReplying}
              >
                <ChatsCircle className="h-3 w-3" />
                Reply
              </button>
              
              {/* Edit button - only for comment owner or admins */}
              {(session?.user?.id === comment.user.id || 
                session?.user?.role === 'ADMIN' || 
                session?.user?.role === 'SUPERADMIN') && (
                <button
                  type="button"
                  onClick={() => onEdit(comment.id)}
                  className="flex items-center gap-1 text-xs text-gray-500 hover:text-blue-600 transition-colors"
                  disabled={isEditing}
                >
                  <PencilSimple className="h-3 w-3" />
                  Edit
                </button>
              )}
              
              {/* Delete button - only for comment owner or admins */}
              {(session?.user?.id === comment.user.id || 
                session?.user?.role === 'ADMIN' || 
                session?.user?.role === 'SUPERADMIN') && (
                <button
                  type="button"
                  onClick={() => {
                    if (confirm('Are you sure you want to delete this comment?')) {
                      onDelete(comment.id);
                    }
                  }}
                  className="flex items-center gap-1 text-xs text-gray-500 hover:text-red-600 transition-colors"
                >
                  <Trash className="h-3 w-3" />
                  Delete
                </button>
              )}
              
              {/* Report button - for everyone except comment owner */}
              {session?.user?.id !== comment.user.id && (
                <button
                  type="button"
                  onClick={() => handleReportComment(comment.id)}
                  className="flex items-center gap-1 text-xs text-gray-500 hover:text-orange-600 transition-colors"
                >
                  <Flag className="h-3 w-3" />
                  Report
                </button>
              )}
            </div>
            {isReplying && !comment.isDeleted && (
              <div className="mt-3 p-4 bg-gray-50 rounded-lg border border-gray-200">
                <div className="flex items-start gap-3">
                  <div className="flex-shrink-0">
                    <div className="w-8 h-8 bg-gradient-to-br from-blue-500 to-purple-600 rounded-full flex items-center justify-center text-white font-semibold text-sm">
                      {session?.user?.image ? (
                        <img 
                          src={session.user.image} 
                          alt={session.user.name || 'User'}
                          className="w-8 h-8 rounded-full object-cover"
                        />
                      ) : (
                        <UserCircle className="h-4 w-4" />
                      )}
                    </div>
                  </div>
                  <div className="flex-1 space-y-2">
                    <input
                      type="text"
                      ref={(el) => {
                        replyInputRefs.current[comment.id] = el;
                      }}
                      className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent text-sm"
                      placeholder="Write your reply..."
                      defaultValue=""
                      aria-label="Reply input"
                    />
                    <div className="flex gap-2">
                      <button
                        type="button"
                        className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 text-sm font-medium transition-colors"
                        onClick={async () => {
                          const inputElement = replyInputRefs.current[comment.id];
                          const inputValue = inputElement?.value || '';
                          if (!inputValue.trim()) return;
                          
                          setIsSubmitting(true);
                          try {
                            const res = await fetch(getApiEndpoint(), {
                              method: 'POST',
                              headers: { 'Content-Type': 'application/json' },
                              body: JSON.stringify({
                                content: inputValue.trim(),
                                parentId: comment.id,
                              }),
                            });
                            if (!res.ok) throw new Error('Failed to post reply');
                            
                            // Clear the input
                            if (inputElement) {
                              inputElement.value = '';
                            }
                            setReplyingTo(null);
                            await fetchComments();
                            toast.success('Reply posted!');
                          } catch (err) {
                            toast.error('Failed to post reply');
                          } finally {
                            setIsSubmitting(false);
                          }
                        }}
                        disabled={isSubmitting}
                        aria-label="Post reply"
                      >
                        {isSubmitting ? 'Posting...' : 'Post Reply'}
                      </button>
                      <button
                        type="button"
                        className="px-4 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 text-sm font-medium transition-colors"
                        onClick={() => {
                          setReplyingTo(null);
                          setReplyContent(prev => ({ ...prev, [comment.id]: '' }));
                        }}
                        disabled={isSubmitting}
                        aria-label="Cancel reply"
                      >
                        Cancel
                      </button>
                    </div>
                  </div>
                </div>
              </div>
            )}
          </div>
        </div>
        {/* Recursive Replies */}
        {(Array.isArray(comment.replies) && comment.replies.length > 0) && (
          <div className="mt-4 space-y-3">
            {comment.replies?.map((reply) => (
              <CommentCard
                key={reply.id}
                comment={reply}
                depth={depth + 1}
                onReply={(id) => {
                  setReplyingTo(id);
                  setReplyContent({ [id]: '' });
                }}
                onEdit={(id) => {
                  setEditingComment(id);
                  setEditContent(reply.content);
                }}
                onDelete={(id) => {
                  if (confirm('Are you sure you want to delete this reply?')) {
                    handleDeleteReply(comment.id, reply.id);
                  }
                }}
                onReact={(id, reaction) => handleReactionClick(id, reaction)}
                isReplying={replyingTo === reply.id}
                isEditing={editingComment === reply.id}
                parentId={comment.id}
              />
            ))}
          </div>
        )}
      </div>
    );
  };

  const handleLoadMore = () => {
    setLoadingMore(true);
    setPage(prev => prev + 1);
  };

  useEffect(() => {
    if (page > 1) fetchComments();
    // eslint-disable-next-line
  }, [page]);

  // Helper to recursively remove a comment by id from a nested comment tree
  function removeCommentById(comments: Comment[], id: string): Comment[] {
    return comments
      .filter(comment => comment.id !== id)
      .map(comment => ({
        ...comment,
        replies: removeCommentById(comment.replies, id)
      }));
  }

  const handleDeleteComment = async (id: string) => {
    setIsSubmitting(true);
    try {
      const res = await fetch(`${getApiEndpoint()}?commentId=${id}`, {
        method: 'DELETE',
      });
      if (!res.ok) throw new Error('Failed to delete comment');
      setComments(prev => removeCommentById(prev, id));
      toast.success('Comment deleted!');
      // Optionally, re-fetch in background for consistency
      fetchComments();
    } catch (err) {
      toast.error('Failed to delete comment');
    } finally {
      setIsSubmitting(false);
    }
  };

  const handleDeleteReply = async (parentId: string, replyId: string) => {
    setIsSubmitting(true);
    try {
      const res = await fetch(`${getApiEndpoint()}?commentId=${replyId}`, {
        method: 'DELETE',
      });
      if (!res.ok) throw new Error('Failed to delete reply');
      setComments(prev => removeCommentById(prev, replyId));
      toast.success('Reply deleted!');
      fetchComments();
    } catch (err) {
      toast.error('Failed to delete reply');
    } finally {
      setIsSubmitting(false);
    }
  };

  const handleReportComment = async (commentId: string) => {
    if (!session) {
      toast.error('Please login to report comments');
      return;
    }
    try {
      const response = await fetch(`${getApiEndpoint()}/${commentId}/report`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ userId: session.user?.id }),
      });
      if (response.ok) {
        toast.success('Comment reported!');
        fetchComments(); // Refresh to show updated comment
      } else {
        toast.error('Failed to report comment');
      }
    } catch (error) {
      console.error('Error reporting comment:', error);
      toast.error('Error reporting comment');
    }
  };

  // Helper to deduplicate comments by id
  function dedupeComments(comments: Comment[]): Comment[] {
    const seen = new Set<string>();
    const deduped: Comment[] = [];
    for (const comment of comments) {
      if (!seen.has(comment.id)) {
        seen.add(comment.id);
        deduped.push(comment);
      }
    }
    return deduped;
  }

  // Define a new ALL_REACTIONS array with both emojis and icons, grouped
  const ALL_REACTIONS = [
    // Fun Emoji Reactions (matching API types)
    { type: 'like', emoji: '👍', color: '#22c55e', label: 'Like' },
    { type: 'love', emoji: '❤️', color: '#ef4444', label: 'Love' },
    { type: 'laugh', emoji: '😂', color: '#fbbf24', label: 'Laugh' },
    { type: 'wow', emoji: '😮', color: '#3b82f6', label: 'Wow' },
    { type: 'sad', emoji: '😢', color: '#3b82f6', label: 'Sad' },
    { type: 'angry', emoji: '😠', color: '#dc2626', label: 'Angry' },
    { type: 'dislike', emoji: '👎', color: '#6b7280', label: 'Dislike' },
    { type: 'star', emoji: '⭐', color: '#facc15', label: 'Star' },
    // Justice & Law Icons (matching API types)
    { type: 'justice', icon: Scales, color: '#14b8a6', label: 'Justice' },
    { type: 'gavel', icon: Gavel, color: '#6b7280', label: 'Gavel' },
    { type: 'shield', icon: Shield, color: '#dc2626', label: 'Protection' },
    // Additional fun reactions
    { type: 'party', emoji: '🎉', color: '#a21caf', label: 'Party' },
    { type: 'celebrate', emoji: '🥳', color: '#a21caf', label: 'Celebrate' },
    { type: 'think', emoji: '🤔', color: '#38bdf8', label: 'Think' },
    { type: 'pray', emoji: '🙏', color: '#2563eb', label: 'Pray' },
    { type: 'clap', emoji: '👏', color: '#22c55e', label: 'Clap' },
    // Additional justice icons
    { type: 'trophy', icon: Trophy, color: '#eab308', label: 'Victory' },
    { type: 'handshake', icon: Handshake, color: '#2563eb', label: 'Agreement' },
    { type: 'money', icon: CurrencyDollar, color: '#22c55e', label: 'Money' },
    { type: 'shield-check', icon: ShieldCheck, color: '#22c55e', label: 'Verified' },
    { type: 'medal', icon: Medal, color: '#eab308', label: 'Achievement' },
    { type: 'crown', icon: Crown, color: '#facc15', label: 'Authority' },
    { type: 'sword', icon: Sword, color: '#dc2626', label: 'Justice' },
    { type: 'target', icon: Target, color: '#ef4444', label: 'Target' },
    { type: 'check', icon: CheckCircleIcon, color: '#22c55e', label: 'Approved' },
    { type: 'document', icon: FileText, color: '#3b82f6', label: 'Document' },
    { type: 'scroll', icon: Scroll, color: '#8b5cf6', label: 'Legal' },
    { type: 'court', icon: Buildings, color: '#6b7280', label: 'Court' },
    { type: 'graduation', icon: GraduationCap, color: '#2563eb', label: 'Expert' }
  ];

  // Separate emoji and icon reactions for easier rendering
  const EMOJI_REACTIONS = ALL_REACTIONS.filter(r => r.emoji);
  const ICON_REACTIONS = ALL_REACTIONS.filter(r => r.icon);

  // Add a color map for each reaction type
  const REACTION_COLORS = {
    like: "#22c55e",
    love: "#ef4444",
    laugh: "#fbbf24",
    sad: "#3b82f6",
    fire: "#f97316",
    star: "#facc15",
    celebrate: "#a21caf",
    angry: "#dc2626",
    dislike: "#6b7280",
    curious: "#38bdf8",
    confused: "#ec4899",
    trophy: "#eab308",
    money: "#22c55e",
    gavel: "#6b7280",
    scales: "#14b8a6",
    handshake: "#2563eb"
  };

  // Replace ReactionUserListPopover with a custom dropdown
  const ReactionUserListPopover = ({ users }: { users: { id: string; name: string; username?: string; profilePicture?: string }[] }) => {
    const [open, setOpen] = React.useState(false);
    const timeoutRef = React.useRef<NodeJS.Timeout | null>(null);
    
    const handleMouseEnter = () => {
      if (timeoutRef.current) {
        clearTimeout(timeoutRef.current);
        timeoutRef.current = null;
      }
      setOpen(true);
    };
    
    const handleMouseLeave = () => {
      timeoutRef.current = setTimeout(() => {
        setOpen(false);
      }, 300); // 300ms delay before closing
    };
    
    React.useEffect(() => {
      return () => {
        if (timeoutRef.current) {
          clearTimeout(timeoutRef.current);
        }
      };
    }, []);
    
    return (
      <span style={{ position: 'relative', display: 'inline-block' }}>
        <span
          style={{ 
            cursor: 'pointer', 
            textDecoration: 'underline', 
            color: '#2563eb',
            padding: '2px 4px',
            borderRadius: '4px',
            transition: 'background-color 0.2s ease'
          }}
          onClick={() => setOpen(v => !v)}
          onMouseEnter={handleMouseEnter}
          onMouseLeave={handleMouseLeave}
          onMouseOver={(e) => {
            e.currentTarget.style.backgroundColor = '#f0f9ff';
          }}
          onMouseOut={(e) => {
            e.currentTarget.style.backgroundColor = 'transparent';
          }}
        >
          {users.length} 👥
        </span>
        {open && (
          <div
            style={{
              position: 'absolute',
              top: '100%',
              left: 0,
              background: 'white',
              border: '1px solid #e5e7eb',
              borderRadius: 8,
              boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
              zIndex: 100,
              minWidth: 180,
              padding: 12,
              marginTop: 4
            }}
            onMouseEnter={handleMouseEnter}
            onMouseLeave={handleMouseLeave}
          >
            {users.map(user => (
              <div key={user.id} style={{ 
                display: 'flex', 
                alignItems: 'center', 
                marginBottom: 8,
                padding: '4px 8px',
                borderRadius: '4px',
                transition: 'background-color 0.2s ease'
              }}
              onMouseEnter={(e) => {
                e.currentTarget.style.backgroundColor = '#f3f4f6';
              }}
              onMouseLeave={(e) => {
                e.currentTarget.style.backgroundColor = 'transparent';
              }}
              >
                {user.profilePicture && (
                  <img src={user.profilePicture} alt={user.name} style={{ width: 24, height: 24, borderRadius: '50%', marginRight: 10 }} />
                )}
                <Link 
                  href={user.username ? `/profile/${user.username}` : `/profiles/${user.id}`} 
                  style={{ 
                    color: '#2563eb', 
                    textDecoration: 'none',
                    fontWeight: 500,
                    fontSize: '14px',
                    padding: '4px 0',
                    display: 'block',
                    width: '100%'
                  }}
                  className="hover:text-blue-700 hover:underline transition-colors"
                  onClick={(e) => {
                    e.stopPropagation();
                    // Add a small delay to ensure the link is processed
                    setTimeout(() => {
                      window.location.href = user.username ? `/profile/${user.username}` : `/profiles/${user.id}`;
                    }, 50);
                  }}
                >
                  {user.name}
                </Link>
              </div>
            ))}
          </div>
        )}
      </span>
    );
  };

  // Replace ReactionPopover with a custom dropdown
  const ReactionPopover = ({ onReact, comment }: { onReact: (id: string, type: string) => void, comment: Comment }) => {
    const [open, setOpen] = React.useState(false);
    const [position, setPosition] = React.useState<'bottom' | 'top'>('bottom');
    const buttonRef = React.useRef<HTMLButtonElement>(null);
    const popoverRef = React.useRef<HTMLDivElement>(null);
    
    const handleClick = () => {
      if (!open) {
        // Check if there's enough space below, if not, position above
        if (buttonRef.current) {
          const rect = buttonRef.current.getBoundingClientRect();
          const spaceBelow = window.innerHeight - rect.bottom;
          const spaceAbove = rect.top;
          setPosition(spaceBelow < 300 && spaceAbove > spaceBelow ? 'top' : 'bottom');
        }
      }
      setOpen(v => !v);
    };
    
    React.useEffect(() => {
      const handleClickOutside = (event: MouseEvent) => {
        if (popoverRef.current && !popoverRef.current.contains(event.target as Node) &&
            buttonRef.current && !buttonRef.current.contains(event.target as Node)) {
          setOpen(false);
        }
      };
      
      document.addEventListener('mousedown', handleClickOutside);
      return () => document.removeEventListener('mousedown', handleClickOutside);
    }, []);
    
    return (
      <span style={{ position: 'relative', display: 'inline-block' }}>
        <button
          ref={buttonRef}
          type="button"
          style={{ 
            background: 'none', 
            border: 'none', 
            cursor: 'pointer', 
            color: '#6b7280', 
            fontSize: 18, 
            padding: 4, 
            marginLeft: 4,
            borderRadius: '4px',
            transition: 'background-color 0.2s ease'
          }}
          aria-label="Add Reaction"
          onClick={handleClick}
          onMouseEnter={(e) => {
            e.currentTarget.style.backgroundColor = '#f3f4f6';
          }}
          onMouseLeave={(e) => {
            e.currentTarget.style.backgroundColor = 'transparent';
          }}
        >
          <span style={{ fontSize: 18, marginRight: 2 }}>+</span>
          <span role="img" aria-label="Add Reaction">🙂</span>
        </button>
        {open && (
          <div
            ref={popoverRef}
            style={{
              position: 'absolute',
              [position === 'bottom' ? 'top' : 'bottom']: '100%',
              left: position === 'bottom' ? 0 : 'auto',
              right: position === 'top' ? 0 : 'auto',
              background: 'white',
              border: '1px solid #e5e7eb',
              borderRadius: 8,
              boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
              zIndex: 1000,
              minWidth: 280,
              maxWidth: 320,
              padding: 12,
              marginTop: position === 'bottom' ? 4 : 0,
              marginBottom: position === 'top' ? 4 : 0,
              display: 'flex',
              flexDirection: 'column',
              gap: 8
            }}
          >
            <div style={{ 
              marginBottom: 8, 
              borderBottom: '1px solid #eee', 
              paddingBottom: 4, 
              fontWeight: 600, 
              color: '#888',
              fontSize: '14px'
            }}>
              😊 Fun Reactions
            </div>
            <div style={{ 
              display: 'grid', 
              gridTemplateColumns: 'repeat(4, 1fr)', 
              gap: 6, 
              marginBottom: 12 
            }}>
              {ALL_REACTIONS.filter(r => r.emoji).map(r => (
                <button
                  key={r.type}
                  type="button"
                  style={{
                    background: userHasReacted(comment, r.type) ? r.color : 'white',
                    color: userHasReacted(comment, r.type) ? 'white' : '#374151',
                    border: '1px solid #e5e7eb',
                    borderRadius: 8,
                    padding: '8px 6px',
                    cursor: 'pointer',
                    fontWeight: userHasReacted(comment, r.type) ? 700 : 400,
                    fontSize: 20,
                    minHeight: '40px',
                    display: 'flex',
                    alignItems: 'center',
                    justifyContent: 'center',
                    transition: 'all 0.2s ease',
                    flexDirection: 'column',
                    gap: 2
                  }}
                  onClick={() => { onReact(comment.id, r.type); setOpen(false); }}
                  onMouseEnter={(e) => {
                    e.currentTarget.style.transform = 'scale(1.05)';
                    e.currentTarget.style.boxShadow = '0 2px 8px rgba(0,0,0,0.1)';
                  }}
                  onMouseLeave={(e) => {
                    e.currentTarget.style.transform = 'scale(1)';
                    e.currentTarget.style.boxShadow = 'none';
                  }}
                  aria-label={r.label}
                >
                  <span style={{ 
                    filter: userHasReacted(comment, r.type) ? 'drop-shadow(0 0 2px ' + r.color + ')' : 'none',
                    fontSize: '18px'
                  }}>
                    {r.emoji}
                  </span>
                  <span style={{ 
                    fontSize: '10px', 
                    fontWeight: 500,
                    color: userHasReacted(comment, r.type) ? 'white' : '#6b7280'
                  }}>
                    {r.label}
                  </span>
                </button>
              ))}
            </div>
            <div style={{ 
              marginBottom: 8, 
              borderBottom: '1px solid #eee', 
              paddingBottom: 4, 
              fontWeight: 600, 
              color: '#888',
              fontSize: '14px'
            }}>
              ⚖️ Justice & Law
            </div>
            <div style={{ 
              display: 'grid', 
              gridTemplateColumns: 'repeat(3, 1fr)', 
              gap: 6 
            }}>
              {ALL_REACTIONS.filter(r => r.icon).map(r => {
                const Icon = r.icon;
                return Icon ? (
                  <button
                    key={r.type}
                    type="button"
                    style={{
                      background: userHasReacted(comment, r.type) ? r.color : 'white',
                      color: userHasReacted(comment, r.type) ? 'white' : '#374151',
                      border: '1px solid #e5e7eb',
                      borderRadius: 8,
                      padding: '8px 6px',
                      cursor: 'pointer',
                      fontWeight: userHasReacted(comment, r.type) ? 700 : 400,
                      display: 'flex',
                      alignItems: 'center',
                      justifyContent: 'center',
                      gap: 4,
                      minHeight: '40px',
                      transition: 'all 0.2s ease',
                      flexDirection: 'column'
                    }}
                    onClick={() => { onReact(comment.id, r.type); setOpen(false); }}
                    onMouseEnter={(e) => {
                      e.currentTarget.style.transform = 'scale(1.05)';
                      e.currentTarget.style.boxShadow = '0 2px 8px rgba(0,0,0,0.1)';
                    }}
                    onMouseLeave={(e) => {
                      e.currentTarget.style.transform = 'scale(1)';
                      e.currentTarget.style.boxShadow = 'none';
                    }}
                    aria-label={r.label}
                  >
                    <Icon size={16} color={userHasReacted(comment, r.type) ? 'white' : r.color} weight={userHasReacted(comment, r.type) ? 'fill' : 'regular'} />
                    <span style={{ 
                      fontSize: '10px', 
                      fontWeight: 500,
                      color: userHasReacted(comment, r.type) ? 'white' : '#6b7280'
                    }}>
                      {r.label}
                    </span>
                  </button>
                ) : null;
              })}
            </div>
          </div>
        )}
      </span>
    );
  };

  // Memoize CommentCard to avoid unnecessary re-renders
  const MemoizedCommentCard = React.memo(CommentCard);

  // Wrap the comment input area with RequireAuth
  return (
    <div className="space-y-6">
      {/* Header with Stats and Controls */}
      <div className="bg-white rounded-lg border border-gray-200 p-6">
        <div className="flex items-center justify-between mb-4">
          <div className="flex items-center gap-4">
            <h3 className="text-lg font-semibold text-gray-900 flex items-center gap-2">
              <ChatCircle className="h-5 w-5 text-blue-600" />
              Comments & Discussion
            </h3>
            <div className="flex items-center gap-2 text-sm text-gray-500">
              <UserCircle className="h-4 w-4" />
              {comments.length} comments
            </div>
          </div>
          
          <div className="flex items-center gap-2">
            <button
              type="button"
              onClick={() => setShowAdvancedOptions(!showAdvancedOptions)}
              className="flex items-center gap-1 px-3 py-1 text-sm text-gray-600 hover:text-gray-800 hover:bg-gray-100 rounded-lg transition-colors"
            >
              <MagnifyingGlassPlus className="h-4 w-4" />
              Filters
            </button>
          </div>
        </div>

        {/* Advanced Options */}
        <AnimatePresence>
          {showAdvancedOptions && (
            <motion.div
              initial={{ opacity: 0, height: 0 }}
              animate={{ opacity: 1, height: 'auto' }}
              exit={{ opacity: 0, height: 0 }}
              className="border-t border-gray-200 pt-4 space-y-4"
            >
              <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
                {/* Sort */}
                <div>
                  <label className="block text-sm font-medium text-gray-700 mb-1">Sort by</label>
                  <select
                    value={sortBy}
                    onChange={(e) => setSortBy(e.target.value as any)}
                    className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent text-sm"
                  >
                    <option value="newest">Newest first</option>
                    <option value="oldest">Oldest first</option>
                    <option value="mostLiked">Most liked</option>
                    <option value="mostReplies">Most replies</option>
                  </select>
                </div>

                {/* Filter */}
                <div>
                  <label className="block text-sm font-medium text-gray-700 mb-1">Filter by</label>
                  <select
                    value={filterBy}
                    onChange={(e) => setFilterBy(e.target.value as any)}
                    className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent text-sm"
                  >
                    <option value="all">All users</option>
                    <option value="lawyers">Lawyers only</option>
                    <option value="clients">Clients only</option>
                    <option value="admins">Admins only</option>
                  </select>
                </div>

                {/* Search */}
                <div>
                  <label className="block text-sm font-medium text-gray-700 mb-1">Search</label>
                  <input
                    type="text"
                    value={searchTerm}
                    onChange={(e) => setSearchTerm(e.target.value)}
                    placeholder="Search comments..."
                    className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent text-sm"
                  />
                </div>
              </div>
            </motion.div>
          )}
        </AnimatePresence>
      </div>

      {/* Comment Input */}
      <RequireAuth>
        <div className="bg-white rounded-lg border border-gray-200 p-6">
          <h3 className="text-lg font-semibold text-gray-900 mb-4 flex items-center gap-2">
            <ChatsCircle className="h-5 w-5 text-blue-600" />
            Join the Discussion
          </h3>
          
          <div className="flex items-start gap-3">
            <div className="flex-shrink-0">
              <div className="w-10 h-10 bg-gradient-to-br from-blue-500 to-purple-600 rounded-full flex items-center justify-center text-white font-semibold">
                {session?.user?.image ? (
                  <img 
                    src={session.user.image} 
                    alt={session.user.name || 'User'}
                    className="w-10 h-10 rounded-full object-cover"
                  />
                ) : (
                  <UserCircle className="h-5 w-5" />
                )}
              </div>
            </div>
            
            <div className="flex-1 space-y-3">
              <form onSubmit={(e) => e.preventDefault()} role="form" aria-label="Add a comment">
                <textarea
                  ref={textareaRef}
                  value={newComment}
                  onChange={(e) => setNewComment(e.target.value)}
                  onKeyDown={(e) => {
                    if (e.key === 'Enter' && e.ctrlKey) {
                      e.preventDefault();
                      handleAddComment();
                    }
                  }}
                  placeholder="Share your thoughts on this case... (Ctrl+Enter to submit)"
                  className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent resize-none"
                  rows={4}
                  aria-label="Comment input"
                />
              </form>
              
              {/* Attachments */}
              {allowAttachments && (
                <div className="space-y-2">
                  <input
                    ref={fileInputRef}
                    type="file"
                    multiple
                    onChange={handleFileUpload}
                    className="hidden"
                    accept="image/*,.pdf,.txt"
                  />
                  
                  <div className="flex items-center gap-2">
                    <button
                      type="button"
                      onClick={() => fileInputRef.current?.click()}
                      className="flex items-center gap-1 px-3 py-1 text-sm text-gray-600 hover:text-gray-800 hover:bg-gray-100 rounded-lg transition-colors"
                    >
                      <Paperclip className="h-4 w-4" />
                      Attach files
                    </button>
                    <span className="text-xs text-gray-500">Max 5MB per file</span>
                  </div>
                  
                  {attachments.length > 0 && (
                    <div className="space-y-2">
                      {attachments.map((file, index) => (
                        <div key={index} className="flex items-center gap-2 p-2 bg-gray-50 rounded-lg">
                          <Paperclip className="h-4 w-4 text-gray-500" />
                          <span className="text-sm text-gray-700 flex-1">{file.name}</span>
                          <span className="text-xs text-gray-500">
                            ({(file.size / 1024 / 1024).toFixed(2)} MB)
                          </span>
                          <button
                            type="button"
                            onClick={() => removeAttachment(index)}
                            className="text-red-500 hover:text-red-700"
                          >
                            <XCircle className="h-4 w-4" />
                          </button>
                        </div>
                      ))}
                    </div>
                  )}
                </div>
              )}
              
              <div className="flex justify-between items-center">
                <span className="text-sm text-gray-500">
                  {newComment.length}/1000 characters
                </span>
                <button
                  type="button"
                  onClick={handleAddComment}
                  disabled={!newComment.trim() || isSubmitting}
                  className="flex items-center gap-2 px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
                >
                  {isSubmitting ? (
                    <>
                      <div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white"></div>
                      Posting...
                    </>
                  ) : (
                    <>
                      <PaperPlaneRight className="h-4 w-4" />
                      Post Comment
                    </>
                  )}
                </button>
              </div>
            </div>
          </div>
        </div>
      </RequireAuth>

      {/* Comments List */}
      <div className="space-y-4">
        {loading ? (
          <div className="text-center py-12">
            <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600 mx-auto"></div>
            <p className="text-gray-500 mt-2">Loading comments...</p>
          </div>
        ) : sortedComments.length === 0 ? (
          <div className="text-center py-12 bg-gray-50 rounded-lg">
            <ChatsCircle className="h-12 w-12 text-gray-400 mx-auto mb-3" />
            <p className="text-gray-500">No comments yet. Be the first to share your thoughts!</p>
          </div>
        ) : (
          <div className="space-y-4">
            {sortedComments.map((comment) => (
              <MemoizedCommentCard
                key={comment.id}
                comment={comment}
                depth={0}
                onReply={(id) => {
                  setReplyingTo(id);
                  setReplyContent({ [id]: '' });
                }}
                onEdit={(id) => {
                  setEditingComment(id);
                  setEditContent(comment.content);
                }}
                onDelete={(id) => {
                  if (confirm('Are you sure you want to delete this comment?')) {
                    handleDeleteComment(id);
                  }
                }}
                onReact={handleReactionClick}
                isReplying={replyingTo === comment.id}
                isEditing={editingComment === comment.id}
              />
            ))}
            {hasMore && !loading && (
              <div className="text-center mt-4">
                <button
                  type="button"
                  className="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50"
                  onClick={handleLoadMore}
                  disabled={loadingMore}
                >
                  {loadingMore ? 'Loading...' : 'Load More'}
                </button>
              </div>
            )}
          </div>
        )}
              </div>
      </div>
    );
  };



export default EnhancedComments; 

CasperSecurity Mini