placebo.mk/pwa/src/components/features/comments/CommentItem.tsx
2026-02-22 01:11:20 +01:00

154 lines
5.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState } from 'react';
import { useAuth } from '../../../contexts/AuthContext';
import { useCreateComment } from '../../../queries/comments';
import { Button } from '../../ui/button';
import { Textarea } from '../../ui/textarea';
import { Card, CardContent } from '../../ui/card';
import { format } from 'date-fns';
import { mk } from 'date-fns/locale';
import type { Comment } from '../../../lib/api';
interface CommentItemProps {
comment: Comment;
articleId?: string;
liveBlogId?: string;
depth?: number;
}
export function CommentItem({ comment, articleId, liveBlogId, depth = 0 }: CommentItemProps) {
const { isAuthenticated } = useAuth();
const [showReplyForm, setShowReplyForm] = useState(false);
const [replyContent, setReplyContent] = useState('');
const [isSubmittingReply, setIsSubmittingReply] = useState(false);
const createCommentMutation = useCreateComment();
const handleSubmitReply = async (e: React.FormEvent) => {
e.preventDefault();
if (!replyContent.trim() || !isAuthenticated) return;
setIsSubmittingReply(true);
try {
await createCommentMutation.mutateAsync({
content: replyContent,
articleId,
liveBlogId,
parentCommentId: comment.id,
});
setReplyContent('');
setShowReplyForm(false);
} catch (error) {
console.error('Failed to post reply:', error);
} finally {
setIsSubmittingReply(false);
}
};
// Maximum depth to prevent infinite nesting (optional)
const maxDepth = 5;
const canReply = depth < maxDepth;
return (
<div className={depth > 0 ? 'ml-8 mt-4 border-l-2 border-border pl-4' : ''}>
<Card>
<CardContent className="pt-6">
<div className="flex items-start justify-between mb-4">
<div>
<div className="font-medium">
{comment.user?.username || 'Анонимен корисник'}
</div>
<div className="text-sm text-muted-foreground">
{format(new Date(comment.createdAt), 'dd MMMM yyyy, HH:mm', { locale: mk })}
</div>
</div>
{comment.user?.role === 'admin' && (
<span className="px-2 py-1 text-xs rounded-full bg-primary/10 text-primary">
Администратор
</span>
)}
</div>
<p className="whitespace-pre-wrap mb-4">{comment.content}</p>
{/* Reply button and form */}
{isAuthenticated && canReply && (
<div className="mt-4">
{!showReplyForm ? (
<Button
variant="ghost"
size="sm"
onClick={() => setShowReplyForm(true)}
className="text-sm"
>
Одговори
</Button>
) : (
<div className="mt-4 p-4 border rounded-lg bg-muted/20">
<form onSubmit={handleSubmitReply}>
<Textarea
placeholder="Вашиот одговор..."
value={replyContent}
onChange={(e) => setReplyContent(e.target.value)}
className="min-h-[80px] mb-3"
disabled={isSubmittingReply}
autoFocus
/>
<div className="flex justify-end gap-2">
<Button
type="button"
variant="outline"
size="sm"
onClick={() => {
setShowReplyForm(false);
setReplyContent('');
}}
disabled={isSubmittingReply}
>
Откажи
</Button>
<Button
type="submit"
size="sm"
disabled={!replyContent.trim() || isSubmittingReply}
>
{isSubmittingReply ? 'Поставување...' : 'Постави одговор'}
</Button>
</div>
</form>
</div>
)}
</div>
)}
{/* Reactions (if implemented) */}
{comment.reactions && (
<div className="flex items-center gap-4 mt-4 pt-4 border-t">
<div className="flex items-center gap-1">
<span className="text-sm text-muted-foreground">👍 {comment.reactions.likes}</span>
</div>
<div className="flex items-center gap-1">
<span className="text-sm text-muted-foreground">👎 {comment.reactions.dislikes}</span>
</div>
</div>
)}
</CardContent>
</Card>
{/* Render replies recursively */}
{comment.replies && comment.replies.length > 0 && (
<div className="mt-4">
{comment.replies.map((reply) => (
<CommentItem
key={reply.id}
comment={reply}
articleId={articleId}
liveBlogId={liveBlogId}
depth={depth + 1}
/>
))}
</div>
)}
</div>
);
}