diff --git a/src/components/message/ChatHeader.tsx b/src/components/message/ChatHeader.tsx index 36a20db..aa1c3a2 100644 --- a/src/components/message/ChatHeader.tsx +++ b/src/components/message/ChatHeader.tsx @@ -16,6 +16,7 @@ interface ChatHeaderProps { typingText?: string; onClearChat?: () => void; onDeleteConversation?: () => void; + onReportUser?: () => void; } export function ChatHeader({ @@ -30,6 +31,7 @@ export function ChatHeader({ typingText, onClearChat, onDeleteConversation, + onReportUser, }: ChatHeaderProps) { const [isMenuOpen, setIsMenuOpen] = useState(false); const [isMuted, setIsMuted] = useState(false); @@ -49,9 +51,7 @@ export function ChatHeader({ }, { label: 'Report User', - onClick: () => { - // Placeholder — no backend endpoint yet - }, + onClick: () => onReportUser?.(), }, { label: 'Delete Conversation', diff --git a/src/components/message/MessagingPage.tsx b/src/components/message/MessagingPage.tsx index 3a6383c..738a59c 100644 --- a/src/components/message/MessagingPage.tsx +++ b/src/components/message/MessagingPage.tsx @@ -7,6 +7,7 @@ import { ChatHeader } from './ChatHeader'; import { MessageInput } from './MessageInput'; import { ChatDropdownMenu, type DropdownMenuItem } from './ChatDropdownMenu'; import { NewConversationModal, type ConnectedContact } from './NewConversationModal'; +import { ReportUserModal } from './ReportUserModal'; import { useMessaging } from '@/hooks/useMessaging'; import { connectionRequestsService, type ConnectionRequest } from '@/services/connection-requests.service'; import { uploadService } from '@/services/upload.service'; @@ -159,6 +160,7 @@ export function MessagingPage({ initialConversationId }: { initialConversationId const [isLoadingAgents, setIsLoadingAgents] = useState(false); const [isStartingConversation, setIsStartingConversation] = useState(null); const [isComposeOpen, setIsComposeOpen] = useState(false); + const [isReportModalOpen, setIsReportModalOpen] = useState(false); // Store converted avatar URLs by conversation ID const [conversationAvatarUrls, setConversationAvatarUrls] = useState>({}); // Store converted file URLs by message ID (for S3-stored images/files) @@ -595,6 +597,17 @@ export function MessagingPage({ initialConversationId }: { initialConversationId }} /> + {/* Report user modal */} + {currentConversation && ( + setIsReportModalOpen(false)} + reportedUserId={currentConversation.otherParty?.userId || currentConversation.otherParty?.id || ''} + reportedUserName={currentConversation.otherParty?.name || 'User'} + conversationId={currentConversation.id} + /> + )} +
{/* Header */}
@@ -818,6 +831,7 @@ export function MessagingPage({ initialConversationId }: { initialConversationId typingText={typingUserNames.length > 0 ? `${typingUserNames.join(', ')} is typing...` : undefined} onClearChat={clearMessages} onDeleteConversation={handleDeleteConversation} + onReportUser={() => setIsReportModalOpen(true)} /> {/* Messages area wrapper */} diff --git a/src/components/message/ReportUserModal.tsx b/src/components/message/ReportUserModal.tsx new file mode 100644 index 0000000..27c682b --- /dev/null +++ b/src/components/message/ReportUserModal.tsx @@ -0,0 +1,164 @@ +'use client'; + +import { useState } from 'react'; +import { userReportsService } from '@/services/user-reports.service'; + +const REPORT_REASONS = [ + 'Spam or scam', + 'Harassment or bullying', + 'Inappropriate content', + 'Fake profile', + 'Unprofessional behavior', + 'Other', +]; + +interface ReportUserModalProps { + isOpen: boolean; + onClose: () => void; + reportedUserId: string; + reportedUserName: string; + conversationId?: string; +} + +export function ReportUserModal({ + isOpen, + onClose, + reportedUserId, + reportedUserName, + conversationId, +}: ReportUserModalProps) { + const [selectedReason, setSelectedReason] = useState(''); + const [description, setDescription] = useState(''); + const [isSubmitting, setIsSubmitting] = useState(false); + const [submitted, setSubmitted] = useState(false); + const [error, setError] = useState(''); + + if (!isOpen) return null; + + const handleSubmit = async () => { + if (!selectedReason) { + setError('Please select a reason'); + return; + } + + setIsSubmitting(true); + setError(''); + + try { + await userReportsService.createReport({ + reportedUserId, + conversationId, + reason: selectedReason, + description: description.trim() || undefined, + }); + setSubmitted(true); + } catch (err: unknown) { + const message = err instanceof Error ? err.message : 'Failed to submit report'; + // Check for axios error response + const axiosErr = err as { response?: { data?: { message?: string } } }; + setError(axiosErr?.response?.data?.message || message); + } finally { + setIsSubmitting(false); + } + }; + + const handleClose = () => { + setSelectedReason(''); + setDescription(''); + setSubmitted(false); + setError(''); + onClose(); + }; + + return ( +
+
+ {submitted ? ( + // Success state +
+
+ + + +
+

Report Submitted

+

+ Thank you for reporting. Our team will review this and take appropriate action. +

+ +
+ ) : ( + // Report form + <> +
+

+ Report {reportedUserName} +

+

+ Select a reason for reporting this user +

+
+ +
+ {REPORT_REASONS.map((reason) => ( + + ))} + + {selectedReason && ( +