'use client'; import React, { useEffect, useState, useCallback, useRef } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { MessageSquare, Plus, Send, Clock, CheckCircle2, XCircle, Phone, Paperclip, Mic, MicOff, StickyNote, FileText, User, Shield, Eye, X, MessageCircle, ChevronRight, AlertTriangle, Volume2, Notebook, } from 'lucide-react'; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; import { Badge } from '@/components/ui/badge'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from '@/components/ui/dialog'; import { Separator } from '@/components/ui/separator'; import { ScrollArea } from '@/components/ui/scroll-area'; import { useAppStore } from '@/lib/store'; import { t } from '@/lib/i18n'; import { apiGet, apiPost, apiPut, apiDelete } from '@/lib/api'; import { toast } from 'sonner'; interface CommunicationRoom { id: string; schoolId: string; studentId: string; teacherId: string; roomType: string; status: string; requestedBy: string; acceptedAt: string | null; closedAt: string | null; closeReason: string | null; createdAt: string; updatedAt: string; student?: { id: string; firstName: string; lastName: string }; teacher?: { id: string; firstName: string; lastName: string }; messages?: Array<{ id: string; content: string; messageType: string; createdAt: string; senderId: string; }>; } interface Message { id: string; roomId: string; senderId: string; content: string; messageType: string; fileUrl: string | null; metadata: string | null; isRead: boolean; createdAt: string; sender?: { id: string; firstName: string; lastName: string; role: string }; } export default function CommunicationView() { const currentUser = useAppStore((s) => s.currentUser); const [rooms, setRooms] = useState([]); const [selectedRoomId, setSelectedRoomId] = useState(null); const [isLoading, setIsLoading] = useState(true); const [showRequestDialog, setShowRequestDialog] = useState(false); const fetchRooms = useCallback(async () => { try { const data = await apiGet('/api/communication-rooms'); setRooms(data); } catch (error) { console.error('Failed to fetch rooms:', error); } finally { setIsLoading(false); } }, []); useEffect(() => { fetchRooms(); // Polling every 5 seconds for new messages const interval = setInterval(fetchRooms, 5000); return () => clearInterval(interval); }, [fetchRooms]); const role = currentUser?.role; if (isLoading) { return (
); } const requestedRooms = rooms.filter((r) => r.status === 'requested'); const activeRooms = rooms.filter((r) => r.status === 'active'); const closedRooms = rooms.filter((r) => r.status === 'closed'); return (
{/* Header */}

{t('communication.title')}

{role === 'STUDENT' && t('communication.active_conversations')} {role === 'TEACHER' && t('communication.incoming_requests')} {(role === 'SCHOOL_ADMIN' || role === 'VICE_PRINCIPAL') && t('communication.all_rooms')}

{role === 'STUDENT' && ( )}
{/* Main Layout: Room List + Chat */}
{/* Room List */}
{/* Pending Requests (Teacher) */} {role === 'TEACHER' && requestedRooms.length > 0 && ( {t('communication.pending_requests')} ({requestedRooms.length})
{requestedRooms.map((room) => ( setSelectedRoomId(room.id)} currentUserId={currentUser?.id || ''} role={role} /> ))}
)} {/* Active Rooms */} {t('communication.active_conversations')} ({activeRooms.length}) {activeRooms.length === 0 ? (

{t('communication.no_conversations')}

) : (
{activeRooms.map((room) => ( setSelectedRoomId(room.id)} currentUserId={currentUser?.id || ''} role={role} /> ))}
)}
{/* Closed Rooms */} {closedRooms.length > 0 && ( {t('communication.closed')} ({closedRooms.length})
{closedRooms.map((room) => ( setSelectedRoomId(room.id)} currentUserId={currentUser?.id || ''} role={role} /> ))}
)}
{/* Chat Area */}
{selectedRoomId ? ( ) : (

{t('communication.select_conversation')}

)}
{/* Mobile: Show chat as full-screen overlay */} {selectedRoomId && (
setSelectedRoomId(null)} />
)}
{/* Request Dialog */}
); } function RoomListItem({ room, isSelected, onClick, currentUserId, role, }: { room: CommunicationRoom; isSelected: boolean; onClick: () => void; currentUserId: string; role: string | undefined; }) { const isStudent = room.studentId === currentUserId; const otherPerson = isStudent ? room.teacher : room.student; const lastMessage = room.messages?.[0]; const getStatusColor = () => { switch (room.status) { case 'requested': return 'bg-amber-100 text-amber-700 dark:bg-amber-950/30 dark:text-amber-300'; case 'active': return 'bg-emerald-100 text-emerald-700 dark:bg-emerald-950/30 dark:text-emerald-300'; case 'closed': return 'bg-gray-100 text-gray-700 dark:bg-gray-800 dark:text-gray-300'; default: return ''; } }; const getStatusLabel = () => { switch (room.status) { case 'requested': return t('communication.requested'); case 'active': return t('communication.active'); case 'closed': return t('communication.closed'); default: return room.status; } }; return (
{/* Online/offline status indicator */}

{otherPerson ? `${otherPerson.firstName} ${otherPerson.lastName}` : 'Unknown'}

{lastMessage && (

{lastMessage.content}

)}
{/* Unread count badge */} {room.messages && room.messages.length > 0 && room.status === 'active' && ( {room.messages.length} )} {getStatusLabel()}
); } function ChatArea({ roomId, currentUserId, role, onRefresh, isMobile, onClose, }: { roomId: string; currentUserId: string; role: string | undefined; onRefresh: () => void; isMobile?: boolean; onClose?: () => void; }) { const [messages, setMessages] = useState([]); const [newMessage, setNewMessage] = useState(''); const [isLoadingMessages, setIsLoadingMessages] = useState(true); const [isSending, setIsSending] = useState(false); const [room, setRoom] = useState(null); const [isRecording, setIsRecording] = useState(false); const [teacherNotes, setTeacherNotes] = useState(''); const [showTeacherNotes, setShowTeacherNotes] = useState(false); const messagesEndRef = useRef(null); const mediaRecorderRef = useRef(null); const fetchMessages = useCallback(async () => { try { const data = await apiGet<{ messages: Message[]; total: number }>(`/api/communication-rooms/${roomId}/messages`); setMessages(data.messages); } catch (error) { console.error('Failed to fetch messages:', error); } finally { setIsLoadingMessages(false); } }, [roomId]); const fetchRoom = useCallback(async () => { try { const data = await apiGet(`/api/communication-rooms/${roomId}`); setRoom(data); } catch (error) { console.error('Failed to fetch room:', error); } }, [roomId]); useEffect(() => { fetchMessages(); fetchRoom(); const interval = setInterval(fetchMessages, 5000); return () => clearInterval(interval); }, [fetchMessages, fetchRoom]); useEffect(() => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [messages]); const sendMessage = async () => { if (!newMessage.trim()) return; setIsSending(true); try { await apiPost(`/api/communication-rooms/${roomId}/messages`, { content: newMessage, messageType: 'text', }); setNewMessage(''); fetchMessages(); onRefresh(); } catch (error) { toast.error(t('communication.error_send')); } finally { setIsSending(false); } }; const handleAccept = async () => { try { await apiPut(`/api/communication-rooms/${roomId}`, { status: 'active' }); toast.success(t('communication.conversation_accepted')); fetchRoom(); onRefresh(); } catch (error) { toast.error('Error accepting conversation'); } }; const handleReject = async () => { try { await apiPut(`/api/communication-rooms/${roomId}`, { status: 'rejected' }); toast.success(t('communication.conversation_rejected')); onRefresh(); } catch (error) { toast.error('Error rejecting conversation'); } }; const handleClose = async () => { try { await apiPut(`/api/communication-rooms/${roomId}`, { status: 'closed', closeReason: 'Closed by teacher' }); toast.success(t('communication.conversation_closed')); fetchRoom(); onRefresh(); } catch (error) { toast.error('Error closing conversation'); } }; const startRecording = async () => { try { const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); const mediaRecorder = new MediaRecorder(stream); const audioChunks: BlobPart[] = []; mediaRecorder.ondataavailable = (event) => { audioChunks.push(event.data); }; mediaRecorder.onstop = async () => { const audioBlob = new Blob(audioChunks, { type: 'audio/webm' }); // In a real app, you'd upload this to a server // For now, just send a text message indicating voice note try { await apiPost(`/api/communication-rooms/${roomId}/messages`, { content: t('communication.voice_message'), messageType: 'voice', }); fetchMessages(); onRefresh(); } catch { toast.error(t('communication.error_send')); } stream.getTracks().forEach((track) => track.stop()); }; mediaRecorderRef.current = mediaRecorder; mediaRecorder.start(); setIsRecording(true); } catch { toast.error('Microphone access denied'); } }; const stopRecording = () => { if (mediaRecorderRef.current && isRecording) { mediaRecorderRef.current.stop(); setIsRecording(false); } }; const isStudent = room?.studentId === currentUserId; const otherPerson = isStudent ? room?.teacher : room?.student; const isAdmin = role === 'SCHOOL_ADMIN' || role === 'VICE_PRINCIPAL' || role === 'SUPER_ADMIN'; const formatTime = (dateStr: string) => { const date = new Date(dateStr); const now = new Date(); const isToday = date.toDateString() === now.toDateString(); const yesterday = new Date(now); yesterday.setDate(yesterday.getDate() - 1); const isYesterday = date.toDateString() === yesterday.toDateString(); const time = date.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' }); if (isToday) return `${t('communication.today')} ${time}`; if (isYesterday) return `${t('communication.yesterday')} ${time}`; return `${date.toLocaleDateString()} ${time}`; }; return ( {/* Chat Header */}
{isMobile && onClose && ( )}
{/* Online status */}

{otherPerson ? `${otherPerson.firstName} ${otherPerson.lastName}` : 'Unknown'}

{room?.status === 'requested' ? t('communication.requested') : room?.status === 'active' ? t('communication.active') : t('communication.closed')} {isAdmin && ( {t('communication.read_only')} )}
{role === 'TEACHER' && room?.status === 'requested' && ( <> )} {role === 'TEACHER' && room?.status === 'active' && ( <> )}
{/* Teacher Notes Panel */} {showTeacherNotes && (