206 lines
6.1 KiB
TypeScript
206 lines
6.1 KiB
TypeScript
/**
|
|
* File: CommentComponent.tsx
|
|
* Created by: AI Assistant
|
|
* Date: 2025-12-05
|
|
* Purpose: Comment component with privacy controls for kreatiVortex platform
|
|
* Part of: kreatiVortex - Platform Pembelajaran Tari Online
|
|
*/
|
|
|
|
'use client';
|
|
|
|
import React, { useState } from 'react';
|
|
import { Button } from '@/components/ui/button';
|
|
import CommentForm from './CommentForm';
|
|
import AttachmentDisplay from './AttachmentDisplay';
|
|
import { authClient } from '@/lib/auth-client';
|
|
|
|
interface Comment {
|
|
id: string;
|
|
content: string;
|
|
isPrivate: boolean;
|
|
createdAt: string;
|
|
author: {
|
|
id: string;
|
|
user: {
|
|
name: string;
|
|
image?: string;
|
|
};
|
|
};
|
|
attachments: any[];
|
|
replies?: Comment[];
|
|
}
|
|
|
|
interface CommentComponentProps {
|
|
comment: Comment;
|
|
onReply: (content: string, attachments: any[], isPrivate?: boolean) => void;
|
|
onTogglePrivacy?: (commentId: string) => void;
|
|
postAuthorId?: string;
|
|
classEducatorId?: string;
|
|
level?: number;
|
|
}
|
|
|
|
export default function CommentComponent({
|
|
comment,
|
|
onReply,
|
|
onTogglePrivacy,
|
|
postAuthorId,
|
|
classEducatorId,
|
|
level = 0
|
|
}: CommentComponentProps) {
|
|
const [showReplyForm, setShowReplyForm] = useState(false);
|
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
const [user, setUser] = useState<any>(null);
|
|
|
|
// Get current user
|
|
React.useEffect(() => {
|
|
const getCurrentUser = async () => {
|
|
try {
|
|
const session = await authClient.getSession();
|
|
setUser(session?.data?.user || null);
|
|
} catch (error) {
|
|
console.error('Error getting user session:', error);
|
|
}
|
|
};
|
|
getCurrentUser();
|
|
}, []);
|
|
|
|
const handleReply = async (content: string, attachments: any[]) => {
|
|
setIsSubmitting(true);
|
|
try {
|
|
onReply(content, attachments, comment.isPrivate); // Reply inherits privacy
|
|
setShowReplyForm(false);
|
|
} finally {
|
|
setIsSubmitting(false);
|
|
}
|
|
};
|
|
|
|
const handleTogglePrivacy = () => {
|
|
if (onTogglePrivacy) {
|
|
onTogglePrivacy(comment.id);
|
|
}
|
|
};
|
|
|
|
const canTogglePrivacy = user?.role === 'PENDIDIK' && user.id === classEducatorId;
|
|
const isPrivateComment = comment.isPrivate;
|
|
|
|
return (
|
|
<div className={`${level > 0 ? 'ml-8' : ''} mb-4`}>
|
|
{/* Comment Content */}
|
|
<div className={`
|
|
relative p-4 rounded-lg border transition-all
|
|
${isPrivateComment
|
|
? 'border-amber-300 bg-amber-50'
|
|
: 'border-gray-200 bg-white'
|
|
}
|
|
`}>
|
|
{/* Private Indicator */}
|
|
{isPrivateComment && (
|
|
<div className="absolute top-2 right-2 flex items-center space-x-1">
|
|
<svg className="w-4 h-4 text-amber-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
|
</svg>
|
|
<span className="text-xs font-medium text-amber-600">Private</span>
|
|
</div>
|
|
)}
|
|
|
|
{/* Author Info */}
|
|
<div className="flex items-center space-x-3 mb-3">
|
|
<div className="w-8 h-8 bg-gold-500 rounded-full flex items-center justify-center">
|
|
{comment.author.user.image ? (
|
|
<img
|
|
src={comment.author.user.image}
|
|
alt={comment.author.user.name}
|
|
className="w-8 h-8 rounded-full object-cover"
|
|
/>
|
|
) : (
|
|
<span className="text-xs font-bold text-navy-900">
|
|
{comment.author.user.name.charAt(0).toUpperCase()}
|
|
</span>
|
|
)}
|
|
</div>
|
|
<div>
|
|
<p className="font-medium text-gray-900">{comment.author.user.name}</p>
|
|
<p className="text-xs text-gray-500">
|
|
{new Date(comment.createdAt).toLocaleString('id-ID')}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Comment Text */}
|
|
<div className="mb-3">
|
|
<p className={`text-sm ${isPrivateComment ? 'text-amber-900' : 'text-gray-800'}`}>
|
|
{comment.content}
|
|
</p>
|
|
</div>
|
|
|
|
{/* Attachments */}
|
|
{comment.attachments && comment.attachments.length > 0 && (
|
|
<div className="mb-3">
|
|
<AttachmentDisplay attachments={comment.attachments} />
|
|
</div>
|
|
)}
|
|
|
|
{/* Actions */}
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center space-x-2">
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
onClick={() => setShowReplyForm(!showReplyForm)}
|
|
className="text-xs text-gray-500 hover:text-gray-700"
|
|
>
|
|
Balas
|
|
</Button>
|
|
</div>
|
|
|
|
{/* Privacy Toggle for Educators */}
|
|
{canTogglePrivacy && (
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
onClick={handleTogglePrivacy}
|
|
className={`
|
|
text-xs transition-colors
|
|
${isPrivateComment
|
|
? 'text-amber-600 hover:text-amber-700'
|
|
: 'text-gray-500 hover:text-gray-700'
|
|
}
|
|
`}
|
|
>
|
|
{isPrivateComment ? 'Jadikan Publik' : 'Jadikan Private'}
|
|
</Button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Reply Form */}
|
|
{showReplyForm && (
|
|
<div className="mt-3 ml-4">
|
|
<CommentForm
|
|
onSubmit={handleReply}
|
|
placeholder="Tulis balasan..."
|
|
buttonText="Balas Komentar"
|
|
loading={isSubmitting}
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
{/* Replies */}
|
|
{comment.replies && comment.replies.length > 0 && (
|
|
<div className="mt-4 space-y-3">
|
|
{comment.replies.map((reply) => (
|
|
<CommentComponent
|
|
key={reply.id}
|
|
comment={reply}
|
|
onReply={onReply}
|
|
onTogglePrivacy={onTogglePrivacy}
|
|
postAuthorId={postAuthorId}
|
|
classEducatorId={classEducatorId}
|
|
level={level + 1}
|
|
/>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
} |