import React, { useState, useEffect } from 'react'; import { X, Send, MessageSquare, User, Loader2, Trash2 } from 'lucide-react'; import { io } from 'socket.io-client'; import { useTourStore } from '@/store/useTourStore'; import { ConfirmModal } from './ConfirmModal'; interface Comment { id: string; userName: string; content: string; createdAt: string; userId?: string; } interface CommentModalProps { isOpen: boolean; onClose: () => void; locationId: string; locationName: string; onCommentAdded?: () => void; // Callback to update comment count on parent onCommentDeleted?: () => void; isPublicView?: boolean; // New prop to indicate public view } export const CommentModal: React.FC = ({ isOpen, onClose, locationId, locationName, onCommentAdded, onCommentDeleted, isPublicView = false }) => { const [comments, setComments] = useState([]); const [newComment, setNewComment] = useState(''); const [isLoading, setIsLoading] = useState(false); const [confirmState, setConfirmState] = useState({ open: false, commentId: '' }); const userRole = useTourStore(state => state.userRole); const currentUserId = React.useMemo(() => { try { const user = JSON.parse(localStorage.getItem('user') || '{}'); return user.id; } catch { return null; } }, []); const fetchComments = async () => { setIsLoading(true); try { const token = localStorage.getItem('token'); const headers: Record = {}; if (token) headers['Authorization'] = `Bearer ${token}`; const res = await fetch(`/api/v1/locations/${locationId}/comments`, { headers }); if (res.ok) { const data = await res.json(); setComments(data.map((c: any) => ({ id: c.id, userName: c.user?.name || 'Ẩn danh', content: c.content, createdAt: c.createdAt, userId: c.userId }))); } } catch (error) { console.error('Lỗi khi tải bình luận:', error); } finally { setIsLoading(false); } }; useEffect(() => { if (!isOpen || !locationId) return; fetchComments(); // Lắng nghe bình luận mới qua Proxy (không cần hardcode URL) const socket = io(); socket.emit('joinTour', 'global'); // Hoặc logic join cụ thể socket.on('commentAdded', (newCommentData: any) => { if (newCommentData.locationId === locationId) { setComments(prev => { // Tránh trùng lặp nếu chính mình gửi if (prev.find(c => c.id === newCommentData.id)) return prev; return [...prev, { id: newCommentData.id, userName: newCommentData.user?.name || 'Ẩn danh', content: newCommentData.content, createdAt: newCommentData.createdAt, userId: newCommentData.userId || newCommentData.user?.id }]; }); } }); return () => { socket.disconnect(); }; }, [isOpen, locationId]); const handleSend = async () => { if (!newComment.trim()) return; try { const res = await fetch(`/api/v1/locations/${locationId}/comments`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('token')}` }, body: JSON.stringify({ content: newComment }) }); if (res.ok) { setNewComment(''); fetchComments(); onCommentAdded?.(); } } catch (error) { console.error('Lỗi khi gửi bình luận:', error); } }; const handleDelete = async (commentId: string) => { try { const res = await fetch(`/api/v1/locations/comments/${commentId}`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}` } }); if (res.ok) { setComments(prev => prev.filter(c => c.id !== commentId)); onCommentDeleted?.(); } } catch (error) { console.error('Lỗi khi xóa bình luận:', error); } }; if (!isOpen) return null; return (
{/* Header */}

Bình luận

{locationName}

{/* Comment List */}
{isLoading ? (
) : comments.length === 0 ? (
Chưa có bình luận nào.
) : ( comments.map((c) => (

{c.userName}

{(userRole === 'OWNER' || userRole === 'MANAGER' || c.userId === currentUserId) && ( )}

{c.content}

{new Date(c.createdAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}

)) )}
{/* Input Area */}
setNewComment(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && handleSend()} placeholder={isPublicView ? 'Đăng nhập để bình luận...' : 'Viết bình luận...'} className="flex-1 bg-gray-50 border border-gray-200 rounded-2xl px-4 py-3 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all" />
{ handleDelete(confirmState.commentId); setConfirmState({ open: false, commentId: '' }); }} onCancel={() => setConfirmState({ open: false, commentId: '' })} />
); };